2

我在coliru.stacked-crooked.com上有以下工作代码。

正如static std::false_type check(...)重复的那样,我想知道我们是否可以分解它。例如在基类中。正如Jonathan Wakely所指出的,我在问题底部的尝试使用 Clang 编译,但不使用 GCC。

我尝试了很多可能性,但似乎无法decltype使用 GCC 来继承模板静态函数。

问题:
1. GCC-4.9 在这一点上是否符合 C++11 标准?
2.在继承的模板静态成员函数上使用符合 GCC 的解决方法是什么?decltype

#include <type_traits>
#include <iostream>
#include <string>

template<typename T>
struct is_addable
{
  template<typename U> static std::false_type check(...);
  template<typename U> static auto            check(int) 
       -> decltype( std::declval<U>() + std::declval<U>(), std::true_type{});
  using type = decltype(check<T>(0));
};

template<typename T>
struct is_multiplicable
{
  template<typename U> static std::false_type check(...);
  template<typename U> static auto            check(int)
    -> decltype( std::declval<U>() * std::declval<U>(), std::true_type{});
  using type = decltype(check<T>(0));
};

int main()
{
  std::cout <<"is_addable\n";
  std::cout <<" long:   "<< is_addable<long>::type::value        <<'\n';
  std::cout <<" string: "<< is_addable<std::string>::type::value <<'\n';
  std::cout <<" void:   "<< is_addable<void>::type::value        <<'\n';

  std::cout <<"is_multiplicable\n";
  std::cout <<" long:   "<< is_multiplicable<long>::type::value        <<'\n';
  std::cout <<" string: "<< is_multiplicable<std::string>::type::value <<'\n';
  std::cout <<" void:   "<< is_multiplicable<void>::type::value        <<'\n';
}

我的尝试之一

编辑:如Jonathan Wakelytemplate<typename U>所指出的那样添加

struct default_check
{ 
  template<typename U>
  static std::false_type check(...);
};

template<typename T>
struct is_addable : default_check
{
  using default_check::check;

  template<typename U> static auto check(int) 
       -> decltype( std::declval<U>() + std::declval<U>(), std::true_type{});

  using type = decltype(check<T>(0));
};

GCC-4.9.2 在coliru.stacked-crooked.com上失败

> g++ -std=c++11 -Wall -pedantic -Wextra -Wfatal-errors main.cpp
main.cpp: In instantiation of 'struct is_addable<void>':
main.cpp:38:63:   required from here
main.cpp:19:30: error: no matching function for call to 'is_addable<void>::check(int)'
   using type = decltype(check<T>(0));
                              ^
compilation terminated due to -Wfatal-errors.

Clang-3.4.1 在godbolt.org上成功

> clang++ -std=c++11 -Wall -pedantic -Wextra -Wfatal-errors
4

2 回答 2

4

default_check::check的不是模板,所以你不能这样称呼它check<T>(0)

解决办法就是把它变成一个函数模板:

struct default_check
{
  template<typename T>  // <-- ADD THIS LINE
    static std::false_type check(...);
};

Clang 接受了这一点,但 GCC 仍然不会编译它(不知道为什么),所以我会放弃并在任何你需要的地方default_checkcheck,就像你最初所做的那样。反正更清楚了。

于 2015-03-12T15:06:43.153 回答
4

我将建议通过check在派生类中默认函数模板的类型参数来简化语法:

template<typename T>
struct is_addable : default_check
{
  using default_check::check;

  template<typename U = T> static auto check(int) 
       -> decltype(std::declval<U>() + std::declval<U>(), std::true_type{});

  using type = decltype(check(0));
};

但是clang 和 gcc 在如何解决重载集上存在分歧。GCC 显然不相信check继承自的非模板default_check是可行的。

在任何情况下,特别是如果您将拥有其中几个特征,将“SFINAE 检查二进制操作”分解到模板中似乎更简单:

template<class...>
struct voider { using type = void; };
template<class...Ts>
using void_t = typename voider<Ts...>::type;

template <class T, class U, class BinaryOperation, class Enable = void>
struct is_binop_able_ : std::false_type {};
template <class T, class U, class BinOp>
struct is_binop_able_<T, U, BinOp, void_t<
  decltype(std::declval<BinOp>()(
    std::declval<T>(), std::declval<U>()))>> :
  std::true_type {};

template <class T, class U, class BinOp>
using is_binop_able = typename is_binop_able_<T,U,BinOp>::type;

并为使用实现所需操作的通用函数对象类型实例化该模板的每个所需特征创建一个别名:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
    ->decltype(__VA_ARGS__) { \
      return (__VA_ARGS__); \
  }

struct plus {
  template <class T, class U>
  auto operator()(T t, U u) RETURNS(t + u)
};
template<class T, class U = T>
using is_addable = is_binop_able<T, U, plus>;

struct multiplies {
  template <class T, class U>
  auto operator()(T t, U u) RETURNS(t * u)
};
template<class T, class U = T>
using is_multiplicable = is_binop_able<T, U, multiplies>;

每个 trait 模板的代码稍微少一些,并且您可以方便地获得可重用的通用二进制函数对象类型作为一个很好的副作用。另一个不错的副作用是 GCC 和 clang 都能正确编译它:DEMO

正如@0x499602D2在评论中所说,voidC++14 标准库函数对象的特化几乎与此处实现的通用二进制函数对象完全相同。如果您的库有它们的 C++14 实现,您可以简单地使用它们而不是编写自己的(仅使用 GCC 的DEMOclang 高达 3.6 会爆炸is_multiplicable<std::string>,尽管clang trunk 可以正确编译它):

template<class T, class U = T>
using is_addable = is_binop_able<T, U, std::plus<>>;

template<class T, class U = T>
using is_multiplicable = is_binop_able<T, U, std::multiplies<>>;
于 2015-03-12T16:15:40.173 回答