4

我正在尝试为类的模板化转换运算符指定模板参数,但我似乎无法正确使用语法。

#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 (以及其他问题中的其他人)所发布的那样, A simpletemplate <typename = typename std::enable_if<!std::is_const<T>::value>::type>不起作用,因为当 Ptr 被实例化时, T 在编译器到达此声明时就已经知道了。由于没有推导出 T,因此没有 SFINAE。因为我们知道!is_const<T>::value是假的,所以没有“类型”成员并且声明是无效的。使模板依赖于新类型 (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; }
};

但后来我对自己说,这是一个模板化的成员函数。这些模板参数不必保留为默认值,任何实例化该函数的人都可以指定它们。对于任何其他 operator 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 类只是我能想到的搜索该语法的最简单方法。

4

2 回答 2

1

有点不清楚您要实现什么...实际上没有充分的理由将所有这些成员函数都作为模板,您也可以将它们作为常规函数adder作为参数。

嗯,这个get函数并没有真正得到,而是添加,所以你可以调用它add。函数调用运算符operator()()可以很好地接受一个intas 参数。转换运算符 toint作为模板实际上没有任何意义,并且不能按照定义的方式调用。如果您坚持将getandoperator()作为模板,您可以将它们称为:

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

int但我建议你重新考虑设计,决定你真正需要什么以及模板是否是要走的路......(请注意,即使在非模板版本中,进行转换也没有意义价值,你不是在转换,而是在创造一个不同的int!)

于 2013-03-09T02:36:45.427 回答
0

这是您没有问的问题的答案,如何enable_if对隐式转换运算符执行 SFINAE 操作,根据类本身的模板参数启用或禁用它们:

#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>. 虽然返回 anint_wrapper<...>并不是 an 的完美替代品int,但它非常接近。

于 2013-03-09T01:40:27.413 回答