C++ 如何为类的模板化转换运算符指定参数
c++ how to specify arguments for a class' templated conversion operator
我试图为类的模板化转换运算符指定模板参数,但似乎无法正确理解语法。
#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。由于您没有为转换运算符指定返回类型或方法参数,所以我将enable_if作为方法模板参数的一部分。
正如Yakk(以及其他问题中的其他人)所说,简单的template <typename = typename std::enable_if<!std::is_const<T>::value>::type>
不起作用,因为当Ptr被实例化时,编译器到达该声明时,t是已知的。由于没有推导出T,因此不存在SFINAE。由于我们知道!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函数,执行此操作的语法都是显而易见的并且有效(请参见上面的运算符())。例如:
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已经是常量时将其转换为常量T不会有任何影响),但这是针对这个特定问题的。也许不可能为转换运算符指定模板参数,所以让这些类型被推导/默认是可以的。或者可能有一种语法(icc似乎接受了它…),所以我向用户开放,让他们指定模板参数,并在我不需要的地方实例化方法。我已经为我的特定问题找到了解决方案(在类型确实重要的时候,在转换运算符中的类型检查上使用static_assert),但这个问题是关于C++语言及其语法的。顶部的类C是我能想到的搜索该语法的最简单的方法。
您想要实现的目标有点不清楚。。。确实没有充分的理由将所有这些成员函数都作为模板。通常,您可以将adder
作为参数,使它们成为常规函数。
get
函数并不是真的得到,而是添加的,所以可以称之为add
。函数调用运算符operator()()
可以很好地将int
作为参数。int
的转换运算符作为模板实际上毫无意义,并且不能在定义时调用。如果你坚持把get
和operator()
作为模板,你可以把它们称为:
C c(0);
c.get<5>(); // 5
c<5>(); // 5
但我建议你重新考虑设计,决定你真正需要什么,以及模板是否合适。。。(请注意,即使在非模板化版本中,转换为采用值的int
也没有意义,您不是在转换,而是在创建一个不同的int
!)
以下是您没有问的问题的答案,即如何对隐式强制转换运算符执行SFINAEenable_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参数。除了允许显式地将模板参数传递给operator int_wrapper<n>
之外,这个int参数是完全未使用的。虽然返回int_wrapper<...>
并不是int
的完美替代品,但它非常接近。
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 构造函数和转换运算符之间的重载解析
- 分配给转换运算符失败-C++
- 转换运算符不适用于sleep_until
- 继承模板化转换运算符
- 模板转换运算符在 clang 6 和 clang 7 之间的区别
- 如何在模板化转换运算符中消除此构造的歧义?
- 为什么选择转换运算符的重载?
- 如何避免强制转换运算符 () 和访问运算符 [] 冲突?
- 如果可能的话,C++总是更喜欢右值引用转换运算符而不是常量左值引用吗?
- 了解转换运算符的选择C++
- 多个隐式转换运算符
- 这个typedef和转换运算符语法是什么意思
- 为什么转换运算符调用复制构造函数两次,而等效函数只调用它一次
- 类模板忽略了用户定义的转换运算符(非模板不忽略)
- 为什么在std::for_each()返回时调用转换运算符
- 为什么 std::optional 的强制转换运算符被忽略了
- 使用用户定义的转换运算符推导函数模板参数
- 模板转换运算符的分辨率不明确
- 统一初始化是隐式发生的,即使 int 强制转换运算符是使用 explicit 关键字声明的.原因是什么?