使用GCC在继承的模板静态成员函数上使用decltype

时间:2015-03-12 15:02:19

标签: c++11 inheritance static-methods decltype gcc4.9

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

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

我尝试过很多种可能性,但似乎不可能在使用GCC的继承模板静态函数上使用decltype

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

#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';
}

我的一次尝试

修改已添加template<typename U> Jonathan Wakely指出

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

2 个答案:

答案 0 :(得分:4)

您的default_check::check不是模板,因此您无法像check<T>(0)

那样调用它

解决方案只是使其成为一个功能模板:

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

Clang接受了这一点,但GCC仍然没有编译(不确定原因)所以我只是放弃default_check并在你需要的地方写check,如同你原来的。无论如何,它更清楚。

答案 1 :(得分:4)

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

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 and gcc disagree on how to resolve the overload set。 GCC显然不相信从check继承的非模板default_check是可行的。

在任何情况下,特别是如果您将具有这些特征中的几个,那么分解&#34; SFINAE检查二进制操作似乎更简单&#34;进入模板:

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>;

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

如注释中的@0x499602D2所述,C ++ 14标准库函数对象的void特化与此处实现的通用二元函数对象几乎完全相同。如果你的库有C ++ 14的实现,你可以简单地使用它们而不是编写自己的(DEMO仅使用GCC,clang up to 3.6 blows up on is_multiplicable<std::string>,尽管clang trunk compiles it correctly):

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<>>;