简体   繁体   English

C++ SFINAE 没有失败

[英]C++ SFINAE not failing

Code:代码:

#include <iostream>


using std::nullptr_t;

template<typename... T>
using nullptr_vt = nullptr_t;

struct not_addable{};


template<
  typename T, 
  nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> TSfinae = nullptr>
bool test_addable(int)
{ return true; }

template<typename>
bool test_addable(...)
{ return false; }


int main()
{
  std::cout << std::boolalpha;

  std::cout << test_addable<int>(0) << std::endl;
  std::cout << test_addable<not_addable>(0) << std::endl;

  // Gives error ("invalid operands to binary expression"):
  // nullptr_vt<decltype(std::declval<not_addable>() + std::declval<not_addable>())> a{};
}

I thought this would print:我以为这会打印:

true
false

, but it doesn't. ,但事实并非如此。 It prints:它打印:

true 
true

. . At least on https://repl.it/@Hrle/sfinaetemplatesuccess .至少在https://repl.it/@Hrle/sfinaetemplatesuccess上。

I thought that nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> from the first overload would be an error for not_addable and it would discard it from the overload set, thus choosing the second overload.我认为第一次重载中的nullptr_vt<decltype(std::declval<T>() + std::declval<T>())>将是not_addable的错误,它会从重载集中丢弃它,因此选择第二次超载。

Does the compiler have the ability to discard the type of TSfinae if there is a default?如果有默认值,编译器是否有能力丢弃TSfinae的类型?

I thought that nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> from the first overload would be an error for not_addable and it would discard it from the overload set, thus choosing the second overload.我认为第一次重载中的nullptr_vt<decltype(std::declval<T>() + std::declval<T>())>将是 not_addable 的错误,它会从重载集中丢弃它,因此选择第二次超载。

The idea is actually fine, the problem is just with GCC and nullptr_vt这个想法实际上很好,问题只是 GCC 和nullptr_vt

This line:这一行:

nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> TSfinae = nullptr

works where you don't want it to on GCC 10.2 but is correct on Clang 11.0.1.在 GCC 10.2 上您不希望它在哪里工作,但在 Clang 11.0.1 上是正确的。 Changing it to将其更改为

nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> *TSfinae = nullptr

is correct on both, as are the simpler两者都是正确的,更简单的

typename TSfinae = nullptr_vt<decltype(std::declval<T>() + std::declval<T>())>
typename _ = decltype(std::declval<T>() + std::declval<T>())

And finally the make_void trick最后是 make_void 技巧

template<typename... T> struct make_nullptr_vt { using type = nullptr_t; };

template<typename T>
using nullptr_vt = typename make_nullptr_vt<T>::type;

fixes the original version on GCC as well.也修复了 GCC 上的原始版本。

This doesn't explain the problem, and it does not pretend to be better than @Useless answer, but it is an alternative solution I find convenient.这并不能解释问题,也不会假装比@Useless 答案更好,但它是我觉得方便的替代解决方案。

I replace the typename by an integer in order to save a bit of writing, and use the comma operator in order to enumerate many conditions if necessary.我将typename替换为 integer 以节省一些写作时间,并在必要时使用逗号运算符枚举许多条件。 Of course, an alias declaration with using can help increase readability when the same conditions have to be used many times.当然,当必须多次使用相同的条件时, using的别名声明有助于提高可读性。


EDIT编辑

As suggested by @StoryTeller comment, if we declare an operator, that combines with the last 1 , then that 1 will be consumed and we can emit instead in decltype() a type that will make SFINAE fail.正如@StoryTeller 评论所建议的那样,如果我们声明一个与最后一个1组合的operator,那么该1将被消耗,我们可以在decltype()中发出一个会使 SFINAE 失败的类型。 He suggests inserting a void() in the sequence of conditions just before the 1 .他建议在1之前的条件序列中插入一个void() Actually, it is not possible to declare an operator, without a right-hand-side operand;实际上,如果没有右侧操作数,就不可能声明operator,符。 thus nothing will combine with this void() and finally 1 will be emitted in decltype() .因此没有任何东西会与这个void()结合,最后1将在decltype()中发出。 It's not as minimal as just 1 , but it's safer.它不像1那样最小,但它更安全。

/**
  g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
      -pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

#include <iostream>

struct A
{
  A operator+(A r);
  A operator-(A r);
  A operator,(int r); // try to mislead SFINAE
};

struct B
{
  B operator+(B r);
  // no -
};

struct C
{
  // no +
  // no -
};

template<
  typename T, 
  decltype((std::declval<T>()+std::declval<T>()),
           void(),1) =1>
bool test_add(int)
{ return true; }

template<typename>
bool test_add(...)
{ return false; }

template<
  typename T, 
  decltype((std::declval<T>()+std::declval<T>()),
           (std::declval<T>()-std::declval<T>()),
           void(),1) =1>
bool test_add_sub(int)
{ return true; }

template<typename>
bool test_add_sub(...)
{ return false; }

template<typename T>
using has_add =
  decltype((std::declval<T>()+std::declval<T>()),
           void(),1);

template<typename T>
using has_add_sub =
  decltype((std::declval<T>()+std::declval<T>()),
           (std::declval<T>()-std::declval<T>()),
           void(),1);

template<
  typename T,
  has_add<T> =1> 
bool test_add2(int)
{ return true; }

template<typename>
bool test_add2(...)
{ return false; }

template<
  typename T, 
  has_add_sub<T> =1>
bool test_add_sub2(int)
{ return true; }

template<typename>
bool test_add_sub2(...)
{ return false; }

int main()
{
  std::cout << std::boolalpha;
  std::cout << "test_add<int>(0) " << test_add<int>(0) << '\n';
  std::cout << "test_add<A>(0)   " << test_add<A>(0)   << '\n';
  std::cout << "test_add<B>(0)   " << test_add<B>(0)   << '\n';
  std::cout << "test_add<C>(0)   " << test_add<C>(0)   << '\n';
  std::cout << "test_add_sub<int>(0) " << test_add_sub<int>(0) << '\n';
  std::cout << "test_add_sub<A>(0)   " << test_add_sub<A>(0)   << '\n';
  std::cout << "test_add_sub<B>(0)   " << test_add_sub<B>(0)   << '\n';
  std::cout << "test_add_sub<C>(0)   " << test_add_sub<C>(0)   << '\n';
  std::cout << "test_add2<int>(0) " << test_add2<int>(0) << '\n';
  std::cout << "test_add2<A>(0)   " << test_add2<A>(0)   << '\n';
  std::cout << "test_add2<B>(0)   " << test_add2<B>(0)   << '\n';
  std::cout << "test_add2<C>(0)   " << test_add2<C>(0)   << '\n';
  std::cout << "test_add_sub2<int>(0) " << test_add_sub2<int>(0) << '\n';
  std::cout << "test_add_sub2<A>(0)   " << test_add_sub2<A>(0)   << '\n';
  std::cout << "test_add_sub2<B>(0)   " << test_add_sub2<B>(0)   << '\n';
  std::cout << "test_add_sub2<C>(0)   " << test_add_sub2<C>(0)   << '\n';
  return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM