2014-10-01 71 views
14

我试图消除超载设置,如果operator+=丢失超载。SFINAE检查运营商+ =

我知道如何检查是否T+T是合法的:

template<typename T, 
     typename CheckTplusT = decltype(std::declval<T>() + std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
    a = a + b; 
} 

但这并不为+=

template<typename T, 
     typename CheckTplusT = decltype(std::declval<T>() += std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
    a += b; 
} 

工作,这是可以解决的,通过使用内部decltype另一种表达还是需要另一SFINAE构造?

我需要从重载集中消除这个问题的原因是它与另一个接受函数的超载冲突,以作为+=的替代方案。编译器是VS2013,gcc4.8

+0

你调用'foo'的什么形式不起作用? – 2014-10-01 10:22:28

+0

@PiotrS。 :第二种形式不起作用。你不能在右值'std :: declval ()'上调用+ =(非const方法)。但是你可以在rvalues上调用+。比较2 + 2和2 + = 2 – MSalters 2014-10-01 10:38:46

+1

@MSalters如果'+ ='是一种方法,则可以在rvalues上调用它(除非方法有'&'限定符)。问题出在内置的'+ ='和基本类型。 – dyp 2014-10-01 10:52:55

回答

16

我会写第二种形式为:

template<typename T> 
auto foo(T a, T b, ...) -> decltype(a+=b, void()) 
{ 
    a += b; 
} 

推导类型decltype(a+=b, void())将只是void如果表达式a+=b是有效的,否则它会导致SFINAE。

那么,即使在第一种形式,我会使用尾随返回类型的方法。

+2

接受。由于其他原因(取决于函数参数的返回类型),其余的重载集合已经需要尾随返回类型,所以这个解决方案实际上增加了不同重载之间的相似性。 @ gexicide的解决方案更接近我原来的实现,但这对未来的维护者没有任何好处。 – MSalters 2014-10-01 10:59:10

+0

返回'T&'对于'+ ='语义会更习惯 – TemplateRex 2014-10-01 12:03:42

+0

它应该编译失败。这是无效的,因为A没有+ =(即使你在Foo的调用中不使用临时对象) – CashCow 2014-10-01 12:12:38

1

添加这个main()函数:

int main() 
{ 
    int x = 1, y = 2; 
    foo(x, y); 
} 

这是编译器错误是什么:

main.cpp: In function int main(): main.cpp:15:15: error: no matching 
function for call to foo(int&, int&) 
     foo(x, y); 
      ^main.cpp:15:15: note: candidate is: 
main.cpp:7:6: note: template<class T, class CheckTplusT> void foo(T, T, ...) void 

foo(T a, T b, ...) 
^main.cpp:7:6: note: template argument deduction/substitution failed: 
    main.cpp:6:60: error:  
     using xvalue (rvalue reference) as lvalue 
     typename CheckTplusT = decltype(std::declval<T>() += std::declval<T>())> 

重点线是using xvalue (rvalue reference) as lvalue

这是declval的文档

此workaro UND工作对我来说:

template<typename T, 
    typename CheckTpluseqT = decltype(*std::declval<T*>() += *std::declval<T*>())> 
void foo(T &a, T b, ...) 
{ 
    a += b; 
} 

int main() 
{ 
    int a = 1, b = 2; 
    foo(a, b); 
    std::cout << a << std::endl; 
} 

输出3

您也可以使用,当然declval<T&>

+2

它不回答OP的问题。 – ikh 2014-10-01 10:39:56

1

这个怎么样?这是在std::declval之前使用的方法。

template<typename T, 
     typename CheckTplusT = decltype(*(T*)nullptr += std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
    a += b; 
    std::cout << "foo with +=" << std::endl; 
} 
+1

'std :: declval ()'更清洁。 – Jarod42 2014-10-01 11:30:03

12

你的+=左侧需要一个lvalue但你的解决方案具有的x值。正如dyp在评论中指出的那样,您可以使用declval<T&>来得到一个左值。这工作正常(只是测试它):

template<typename T, 
     typename CheckTplusT = decltype(std::declval<T&>() += std::declval<T>())> 
void foo(T a, T b, ...) 
{ 
} 
+4

根据函数的细节('a + = b'或'a + = move(b)'),第二个'declval'可能也必须使用'T&'。 – dyp 2014-10-01 10:50:48

+0

我知道'std :: declval ()'会工作,但我仍然*不喜欢*解决方案。它看起来很难看。另外,如果'operator + ='采用非const引用参数,那么这个解决方案将不起作用。**尾随返回类型的解决方案更清晰,更好,并且正确*,因为程序员不会必须处理对象的价值范畴。让编译器去做。 – Nawaz 2014-10-01 10:51:06

+3

@Nawaz:对,但它最接近OPs方法。 – gexicide 2014-10-01 10:52:48