шаблоны — c ++, как указать аргументы для оператора шаблонного преобразования класса

Я пытаюсь указать аргументы шаблона для оператора шаблонного преобразования класса, но я не могу понять синтаксис правильно.

#include <iostream>
using namespace std;

class C
{
int i_;
public:
C(int i) : i_(i) {}
template<int adder> int get() { return i_ + adder; }
template<int adder> int operator()() { return i_ + adder; }
template<int adder> operator int() { return i_ + adder; }
// If I add a default argument to operator int()'s adder template parameter this compiles fine
// (of course, I still can't figure out how to specify it...)
};

int main(int, char*[])
{
C c(10);
cout << c.get<2>() << endl;            // I can specify template argument here the regular way.
//   cout << c() << endl;                 // No template argument specified, so I wouldn't have expected this to work.
cout << c.operator()<3>() << endl;     // We have to call it this way.
//    cout << (int)c << endl;             // In the same vein I wouldn't expect this to work either.
cout << c.operator int<4>() << endl;   // But how do I specify template argument here? This seems to be an error for some compilers.
return 0;
}

Тот же код на http://liveworkspace.org/code/35sqXe$4

При компиляции с g ++ 4.7.2

$ g++ -std=c++11 -Wall -W -pedantic "template conversion operator.cpp"
Compilation finished with errors:
source.cpp: In function 'int main(int, char**)':
source.cpp:23:23: error: 'int' is not a template
source.cpp:23:30: error: no matching function for call to 'C::operator int()'
source.cpp:23:30: note: candidate is:
source.cpp:11:24: note: template<int adder> C::operator int()
source.cpp:11:24: note: template argument deduction/substitution failed:
source.cpp:23:30: note: couldn't deduce template parameter 'adder'

При компиляции с g ++ 4.8.0 (20130224)

$ g++ -std=c++11 -Wall -W -pedantic "template conversion operator.cpp"
Compilation finished with errors:
source.cpp: In function 'int main(int, char**)':
source.cpp:23:23: error: 'int' is not a template
cout << c.operator int<4>() << endl;
^
source.cpp:23:30: error: no matching function for call to 'C::operator int()'
cout << c.operator int<4>() << endl;
^
source.cpp:23:30: note: candidate is:
source.cpp:11:24: note: template<int adder> C::operator int()
template<int adder> operator int() { return i_ + adder; }
^
source.cpp:11:24: note: template argument deduction/substitution failed:
source.cpp:23:30: note: couldn't deduce template parameter 'adder'
cout << c.operator int<4>() << endl;
^

При компиляции с clang ++ 3.2

$ clang++ -std=c++11 -Wall -W -pedantic "template conversion operator.cpp"
Compilation finished with errors:
source.cpp:23:12: error: reference to non-static member function must be called
cout << c.operator int<4>() << endl;
^~~~~~~~~~~~~~
source.cpp:23:30: error: expected expression
cout << c.operator int<4>() << endl;
^
2 errors generated.

При компиляции с icc 13.0.1

$ icc -std=c++11 -Wall -W -pedantic "template conversion operator.cpp"
Compilation finished with warnings:
source.cpp(11): warning #488: constant "adder" is not used in declaring the parameter types of function template "C::operator int"template<int adder> operator int() { return i_ + adder; }
^

Кроме того, предупреждение, ICC, кажется, работает нормально.

Это ошибки компилятора? Или проблема в моем синтаксисе?

РЕДАКТИРОВАТЬ

Поскольку Якк спросил, какова была моя оригинальная / актуальная проблема:
У меня был класс Ptr (в зависимости от типа, на который он указывал), и я хотел получить преобразование в Ptr для const T (хотя я знаю, что в данном случае это не имеет значения), я хотел, чтобы оператор преобразования не быть там, если T уже был константным типом. Поскольку вы не указываете тип возвращаемого значения или аргументы метода для оператора преобразования, я включил enable_if как часть параметров шаблона метода.

Как Yakk (и другие в других вопросах) отправил, Простой template <typename = typename std::enable_if<!std::is_const<T>::value>::type> не работает, потому что когда создается экземпляр Ptr, T известен к тому времени, когда компилятор получает это объявление. Поскольку Т не выводится, СФИНА не существует. Так как мы знаем !is_const<T>::value false, нет члена типа и объявление недопустимо. Установление шаблона в зависимости от нового типа (U), вывод U и проверка того, что U совпадает с T, и что T не является константой, а затем наличие недопустимого объявления является допустимым использованием SFINAE и работает как и ожидалось.

template <typename T>
class Ptr
{
template <typename U,
typename = typename std::enable_if<std::is_same<T, U>::value &&
!std::is_const<U>::value>::type>
operator Ptr<const U>() const { return active; }
};

Но потом я сказал себе, что это шаблонная функция-член. Эти аргументы шаблона не должны быть оставлены по умолчанию, их может указать любой, кто создает эту функцию. Для любой другой функции оператора xxx синтаксис для этого очевиден и работает (см. Operator () выше). Для этого примера:

Ptr<const int> ci;
ci.operator Ptr<const int><const int, void>(); // assuming this syntax is valid

Пустота (или любой другой тип) будет указывать второй аргумент шаблона оператора преобразования, и значение по умолчанию, содержащее enable_if, не будет рассматриваться. Это позволило бы этому методу существовать, когда я пытался заставить его не существовать.

Но gcc, clang и msvc, похоже, имеют проблемы с этим синтаксисом. Я полагаю, так как оператор преобразования пишется operator typenameналичие аргументов шаблона приводит к тому, что компилятор вводит в заблуждение мысль, что это имя типа, а не оператора.

Это правда, что есть обходные пути (просто включите оператор преобразования, если преобразование в const T, когда T уже является const, не повредит), но это для этой конкретной проблемы. Возможно, невозможно задать аргументы шаблона для операторов преобразования, поэтому оставить эти типы для вывода / дефолта вполне нормально. Или, может быть, есть синтаксис для этого (кажется, что icc его принимает …), поэтому я открываю себя для пользователей, задающих аргументы шаблона и создающих экземпляры методов там, где они мне не нужны. У меня уже есть решение для моей конкретной проблемы (используйте static_assert при проверке типа в операторе преобразования для случаев, когда тип имеет значение), но этот вопрос касается языка C ++ и его синтаксиса. Класс C вверху — это самый простой способ поиска синтаксиса.

4

Решение

Немного неясно, чего вы пытаетесь достичь … На самом деле нет веских оснований для того, чтобы все эти функции-члены были шаблонами в целом, вы также можете сделать их обычными функциями, принимая adder в качестве аргумента.

get функция, ну не очень получить, скорее добавляет, так что вы можете назвать это add, Оператор вызова функции operator()() вполне может взять int в качестве аргумента. Оператор преобразования в int не имеет буквального смысла в качестве шаблона и не может быть вызван так, как он определен. Если вы настаиваете на том, чтобы get а также operator() в качестве шаблонов вы можете назвать их как:

C c(0);
c.get<5>(); // 5
c<5>();     // 5

Но я предлагаю вам пересмотреть дизайн, решить, что вам действительно нужно, и есть ли шаблоны, по которым стоит идти … (обратите внимание, что даже в не шаблонной версии на самом деле не имеет смысла делать преобразование в int это занимает значение, ты не преобразование, но создавая другой int!)

1

Другие решения

Вот ответ на вопрос, который вы не задавали, как сделать SFINAE enable_if операции над неявными операторами приведения, включающие или отключающие их в зависимости от аргументов шаблона самого класса:

#include <iostream>
#include <type_traits>

template<int n>
struct Foo {
template<typename T,typename=typename std::enable_if<std::is_convertible<T,int>::value && (n!=0)>::type>
operator T() const { return n; }
};

int main() {
Foo<0> zero;
Foo<1> one;
int x = 0;
x = one;
int y = 0;
// y = zero; -- does not compile, because Foo<0> cannot be converted to int
std::cout << x << "," << y << "\n";
}

Это не идеально, как is_convertible означает, что мы генерируем целый ряд неявных преобразований типов, но это относительно близко.

Вот как передать аргумент шаблона в оператор приведения или, по крайней мере, приблизить его:

template<int n>
struct int_wrapper {
int i;
operator int() const { return i; }
int_wrapper( int i_ ):i(i_) {}
};
// in class C:
template<int adder> operator int_wrapper<adder>() { return i_ + adder; }

Здесь я создал игрушечный тип int_wrapper который упаковывает параметр int. Этот параметр int полностью не используется, кроме как для явной передачи параметра шаблона operator int_wrapper<n>, Возвращая int_wrapper<...> не является идеальной заменой intЭто довольно близко.

0