我正在尝试为类的模板化转换运算符指定模板参数,但我似乎无法使语法正确。
#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似乎工作正常。
这些编译器错误吗?或者是我的语法是问题吗?
修改
因为Yakk问我的原始/实际问题是什么:
我有一个类Ptr(根据它指向的类型进行模板化),我希望将Ptr转换为const T.(尽管我知道在这种情况下无关紧要),我希望转换运算符不是如果T已经是const类型,那就在那里。由于您没有为转换运算符指定返回类型或方法参数,因此我将enable_if作为方法模板参数的一部分。
正如Yakk(以及其他问题中的其他人)发布的那样,简单的template <typename = typename std::enable_if<!std::is_const<T>::value>::type>
不起作用,因为当Ptr被实例化时,T在编译器获得此声明时就已知。由于没有推断出T,因此没有SFINAE。由于我们知道!is_const<T>::value
为false,因此没有“type”成员且声明无效。使模板依赖于新类型(U),推导出U,然后检查U是否与T相同,并且T不是const,然后具有无效声明是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
void(或其他任何类型)将指定转换运算符的第二个模板参数,并且不会考虑包含enable_if的默认值。当我试图让它不存在时,这将使这种方法存在。
但是gcc,clang和msvc似乎似乎在使用这种语法时遇到了问题。我假设由于转换运算符拼写为operator typename
,因此使用模板参数会让编译器误以为它们是类型名而不是运算符。
确实存在变通方法(只包括转换运算符,当T已经是const时没有损坏任何转换为const T),但这是针对这个特定问题的。也许不可能为转换运算符指定模板参数,因此保留这些类型可以推导/默认是可以的。或者可能有一个语法(icc似乎采取它...),所以我打开自己指定模板参数和实例化我不想要它们的方法。我已经为我的特定问题找到了解决方案(在转换运算符中对类型确实重要的时候使用static_assert进行类型检查),但这个问题是关于C ++语言及其语法。顶部的C类是我能想到的最简单的搜索语法的方法。
答案 0 :(得分:1)
有点不清楚你想要实现的目标......一般来说,没有任何理由让所有这些成员函数成为模板,你也可以将adder
作为常规函数。一个论点。
get
函数,实际上并不是获取,而是添加,因此您可以将其称为add
。函数调用操作符operator()()
很可能以int
为参数。转换运算符int
在字面上没有意义作为模板,并且无法在定义时调用。如果您坚持将get
和operator()
作为模板,则可以将其称为:
C c(0);
c.get<5>(); // 5
c<5>(); // 5
但我建议您重新考虑设计,确定您真正需要的是什么以及模板是否可行...(请注意,即使在非模板化版本中,转换为非常有意义int
采用值,您不是转换,而是创建不同的int
!)
答案 1 :(得分:0)
以下是您未提出的问题的答案,如何对隐式转换运算符执行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参数的玩具类型int_wrapper
。除了允许将模板参数显式传递给operator int_wrapper<n>
之外,此int参数完全未使用。返回int_wrapper<...>
并不是int
的完美替代,但它非常接近。