C ++ how to specify arguments for a class template conversion operator

I am trying to specify template arguments for a template template conversion operator, but I cannot get the correct syntax.

#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() 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; } 

Same code at http://liveworkspace.org/code/35sqXe$4

When compiling with 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' 

When compiling with 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; ^ 

When compiling with 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. 

When compiling with 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; } ^ 

Other then icc warning works fine.

Are these compiler errors? Or is it my syntax that is the problem?

EDIT

Since Jakk asked what is my original / actual problem:
I had a Ptr class (templated by the type that it pointed to), and I wanted to have the conversion in Ptr to const T. (Although I know that it does not matter in this case), I wanted the conversion operator not to be there if T was already a const type. Since you are not specifying the return type or method arguments for the conversion operator, I did enable_if as part of the method template parameters.

As Yakk (and others on other issues) published, a simple template <typename = typename std::enable_if<!std::is_const<T>::value>::type> does not work, because when Ptr is created, T is known by the time the compiler gets to this declaration. Since T is not output, SFINAE is absent. Since we know that !is_const<T>::value is false, there is no "type" element, and the declaration is invalid. The template definition depends on the new type (U) having U, and then checking that U matches T and that T is not a constant, then having an invalid declaration is a valid use of SFINAE and works as expected.

 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; } }; 

But then I said to myself: this is a template member function. These template arguments should not be left by default; they can be specified by anyone who creates this function. For any other function of the xxx operator, the syntax for this is obvious and works (see Operator () above). In this example:

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

A void (or any other type there) will indicate the argument of the second transform operator pattern, and the default value containing enable_if will not be considered. This will allow this method to be used when I try to make it non-existent.

But gcc, clang and msvc seem to have problems with this syntax. I assume that since the conversion operator typename is written operator typename , the template arguments confuse the compiler in that they are for the type name and not for the operator.

It’s true that there are workarounds (just include a conversion operator that has a conversion to const T when T is no longer const), but this is for this particular problem. It may not be possible to specify template arguments for conversion operators, so leave these types for output / default in order. Or maybe there is a syntax for it (icc seems to take it ...), so I open myself up to users who define template arguments and instance creation methods where I don't want them. I already have a solution for my specific problem (use static_assert to check the type in the conversion statement for the time when the type matters), but this question is about the C ++ language and its syntax. The C class on top is the easiest way I could look for this syntax.

+4
source share
2 answers

It’s a little incomprehensible what you are trying to achieve ... There is no good reason that all these member functions are templates in general, you can also make them regular functions by taking adder as an argument.

The get function, well, actually fails, but adds, so you can call it add . The operator call to operator()() could well accept an int argument. The conversion operator to int literally makes no sense as a template and cannot be called because it is defined. If you insist on using get and operator() patterns as patterns, you can name them as:

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

But I suggest you reconsider the design, decide what you really need, and whether the templates will go ... (note that even in the non-templated version it makes no sense to have a conversion to int , which takes a value, you do not convert , but create another int !)

+1
source

Here is the answer to the question that you did not ask how to perform SFINAE enable_if operations for implicit casting operators, enable or disable them depending on the template arguments to the class itself:

 #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"; } 

This is not ideal, since is_convertible means that we generate a whole chain of implicit type conversions, but it is relatively close.

Here's how to pass a template argument to the casting operator, or at least a way to approximate it:

 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; } 

Here I created a toy type int_wrapper that int_wrapper the int parameter. This int parameter is not fully used, except to explicitly pass the template parameter to operator int_wrapper<n> . When returning int_wrapper<...> not an ideal replacement for int , it is pretty close.

0
source

All Articles