元编程:函数定义失败定义了一个单独的函数

Jon*_*Mee 26 c++ templates result-of sfinae template-meta-programming

这个答案中,我根据类型的is_arithmetic属性定义了一个模板:

template<typename T> enable_if_t<is_arithmetic<T>::value, string> stringify(T t){
    return to_string(t);
}
template<typename T> enable_if_t<!is_arithmetic<T>::value, string> stringify(T t){
    return static_cast<ostringstream&>(ostringstream() << t).str();
}
Run Code Online (Sandbox Code Playgroud)

dyp建议,不是is_arithmetic类型的属性,是否to_string为类型定义是模板选择标准.这显然是可取的,但我不知道如何说:

如果std::to_string未定义,则使用ostringstream重载.

声明to_string标准很简单:

template<typename T> decltype(to_string(T{})) stringify(T t){
    return to_string(t);
}
Run Code Online (Sandbox Code Playgroud)

这与我无法弄清楚如何构建的标准相反.这显然不起作用,但希望它传达了我正在尝试构建的内容:

template<typename T> enable_if_t<!decltype(to_string(T{})::value, string> (T t){
    return static_cast<ostringstream&>(ostringstream() << t).str();
}
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 15

使用Walter Brown的void_t:

template <typename...>
using void_t = void;
Run Code Online (Sandbox Code Playgroud)

制作这种类型特征非常容易:

template<typename T, typename = void>
struct has_to_string
: std::false_type { };

template<typename T>
struct has_to_string<T, 
    void_t<decltype(std::to_string(std::declval<T>()))>
    > 
: std::true_type { };
Run Code Online (Sandbox Code Playgroud)

  • @TartanLlama这是下一个标准的草案.GCC和Clang在使用`-std = c ++ 1z`进行编译时(并且它们的`__void_t`带有`-std = c ++ 11`)和MSVC 2015有它. (4认同)
  • @JonathanMee使用`void_t`的好处是它有效.Matthis Vega的答案[不起作用](http://coliru.stacked-crooked.com/a/b53e7413f82de303). (3认同)
  • 非常优雅,+ 1.您是否碰巧知道是否有很好的标准化机会? (2认同)
  • @TartanLlama 没有线索。至少自己实现起来很容易:) (2认同)
  • @JonathanMee改变什么?我不知道你在说什么.你需要的一切都在巴里的回答中.如果您的标准库实现它,可以用`std :: void_t`或`std :: __ void_t`替换`void_t`,结果将是相同的. (2认同)

Yak*_*ont 14

首先,我认为SFINAE通常应该从接口隐藏.它使界面混乱.将SFINAE远离表面,并使用标签调度来选择过载.

其次,我甚至将SFINAE隐藏在特质类中.在我的经验中写"我可以做X"代码是很常见的,我不想编写凌乱的SFINAE代码来完成它.因此,我写了一个通用can_apply特征,并且如果使用了错误的类型,则具有SFINAE失败的特征decltype.

然后,我们将SFIANE失败decltype特征提供给can_apply,并根据应用程序是否失败取出真/假类型.

这将每个"我可以做X"特性的工作量减少到最小量,并将有些棘手和脆弱的SFINAE代码放在日常工作之外.

我使用C++ 1z void_t.自己实现它很容易(在这个答案的底部).

类似的元函数can_apply被提议用于C++ 1z中的标准化,但它不像那样稳定void_t,所以我没有使用它.

首先,一个details命名空间来隐藏can_apply意外发现的实现:

namespace details {
  template<template<class...>class Z, class, class...>
  struct can_apply:std::false_type{};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:
    std::true_type{};
}
Run Code Online (Sandbox Code Playgroud)

然后,我们可以写can_apply来讲details::can_apply,它有一个更好的接口(它不需要额外的void传递):

template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
Run Code Online (Sandbox Code Playgroud)

以上是通用辅助元编程代码.一旦我们有了它,我们可以can_to_string非常干净地写一个特征类:

template<class T>
using to_string_t = decltype( std::to_string( std::declval<T>() ) );

template<class T>
using can_to_string = can_apply< to_string_t, T >;
Run Code Online (Sandbox Code Playgroud)

我们有一个特质can_to_string<T>是真的当且仅当我们可以to_string一个T.

这项工作需要编写一个新特性,现在是2-4行简单代码 - 只需创建一个decltype using别名,然后对其进行can_apply测试.

一旦我们有了这个,我们就使用标签调度来实现正确的实现:

template<typename T>
std::string stringify(T t, std::true_type /*can to string*/){
  return std::to_string(t);
}
template<typename T>
std::string stringify(T t, std::false_type /*cannot to string*/){
  return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<typename T>
std::string stringify(T t){
  return stringify(t, can_to_string<T>{});
}
Run Code Online (Sandbox Code Playgroud)

所有丑陋的代码都隐藏在details命名空间中.

如果您需要void_t,请使用:

template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
Run Code Online (Sandbox Code Playgroud)

它适用于大多数主要的C++ 11编译器.

请注意,较简单的template<class...>using void_t=void;无法在一些较旧的C++ 11编译器中工作(标准中存在歧义).


T.C*_*.C. 14

在上周的委员会会议上刚刚投票进入图书馆基础TS:

template<class T>
using to_string_t = decltype(std::to_string(std::declval<T>()));

template<class T>
using has_to_string = std::experimental::is_detected<to_string_t, T>;
Run Code Online (Sandbox Code Playgroud)

然后将调度和/或SFINAE标记has_to_string为您心脏的内容.

您可以参考TS的当前工作草案,了解如何实现is_detected和朋友.这与can_apply@ Yakk的答案非常相似.


Tar*_*ama 9

您可以使用表达式SFINAE为此编写一个辅助特征:

namespace detail
{
    //base case, to_string is invalid
    template <typename T>
    auto has_to_string_helper (...) //... to disambiguate call
       -> false_type;

    //true case, to_string valid for T
    template <typename T>
    auto has_to_string_helper (int) //int to disambiguate call
       -> decltype(std::to_string(std::declval<T>()), true_type{});
}

//alias to make it nice to use
template <typename T>
using has_to_string = decltype(detail::has_to_string_helper<T>(0));
Run Code Online (Sandbox Code Playgroud)

然后用 std::enable_if_t<has_to_string<T>::value>

演示