C++ 改变什么?我不知道你在说什么。你需要的一切都在巴里的答案里。您可以用std::void\u t或std::\u void\u t替换void\u t,如果您的标准库实现了它,结果将是相同的。@JonathanMee使用void\u t的好处是它可以工作。M

C++ 改变什么?我不知道你在说什么。你需要的一切都在巴里的答案里。您可以用std::void\u t或std::\u void\u t替换void\u t,如果您的标准库实现了它,结果将是相同的。@JonathanMee使用void\u t的好处是它可以工作。M,c++,templates,template-meta-programming,sfinae,result-of,C++,Templates,Template Meta Programming,Sfinae,Result Of,改变什么?我不知道你在说什么。你需要的一切都在巴里的答案里。您可以用std::void\u t或std::\u void\u t替换void\u t,如果您的标准库实现了它,结果将是相同的。@JonathanMee使用void\u t的好处是它可以工作。Matthis Vega的答案。不应该(ostringstream{}@0x499602D2是的,它将失败。它需要静态(ostringstream{})@0x499602D2是的,看起来确实如此:libc++定义了以下重载:inline\u LI



改变什么?我不知道你在说什么。你需要的一切都在巴里的答案里。您可以用
std::void\u t
std::\u void\u t
替换
void\u t
,如果您的标准库实现了它,结果将是相同的。@JonathanMee使用
void\u t
的好处是它可以工作。Matthis Vega的答案。不应该
(ostringstream{}@0x499602D2是的,它将失败。它需要
静态(ostringstream{})@0x499602D2是的,看起来确实如此:libc++定义了以下重载:
inline\u LIBCPP\u inline\u可见性typename enable\u if::value&&is_base\u of::value,_Stream&&>::typeoperator@Barry它用于阻止用户提供额外的模板参数。请参阅。@JonathanMee
MF
不是函数类型。
MF(int,double)
是一种函数类型。例如
struct MF{};MF my_函数(int,double);
那么
my_函数的类型是
MF(int,double)
而指向
my_函数的指针的类型是
MF(*)(int-double)
MF{
创建一个类型为
MF
的对象,我们调用其成员函数
需要
以查看是否编译。为了将
MF
和参数类型作为单个模板参数传递,我们需要将两者存储在单个类型中。这可以是
元组
或类似
MF(Args…)的函数类型
。我不喜欢这种形式的标记分派,因为
true\u-type
false\u-type
的含义仅由调用者定义。仅从分派目标集测试的内容并不明显。
MF(Args)
form of concepts emulation的优点是,与alias模板相比,它的计算是惰性的。这可以用来防止在直接上下文中不会出现的替换失败。@dyp true。这就是我通常给它一个注释掉的名称的原因。在这两种情况下,都将`/*can添加到字符串*/`中。我不确定是哪种错误你的
MF
方法避免了非即时上下文,而我的方法没有?这是因为
选择
重载还是什么?不,我不确定你是否在标记分派中遇到过它们(与概念模拟相反)。如果有一个复杂的表达式,如
is_complete::value&&is_triple::value
,则必须将其拆分,并确保如果
is_complete
为false,则不会实例化
is_triple
。对于更复杂的特征,
是平凡的
可以是别名模板,因此您必须延迟实例化。OTOH,
是平凡的(T)
不会立即实例化
是平凡的吗
@Yakk你在图书馆基础知识组吗?
std::experimental::is\u detected
是你的
可以应用吗
,差不多。@Barry nope。其他人注意到了这种相似性,并在之前给我看了一个链接。只是我在写这篇文章时感到累了e
void\u t
SFINAE模板样板一次又一次。@Yakk在委员会中吗?@Barry你必须问他:)@t.C.这是否意味着我可以定义我的
ostringstream
重载,比如:
template enable\u if\u t()),t>:value,string>(t){返回静态(ostringstream())@JonathanMee否,
被检测到
的第一个参数是模板参数。您需要一个别名模板。@JonathanMee它是一个模板参数(提示,重复是有意的),它需要一个类或别名模板作为参数。这似乎工作得很好,多亏了重写。看起来
has_to_string_helper
是一个变量模板或什么的,但是
->
在做什么?我不熟悉箭头操作符的用法。
has to_string_helper
是一个模板函数decla定量。我们永远不需要定义它,因为我们只是将它用作编译时构造。
->
使用C++11的尾部返回类型语法。我认为在这种情况下更清楚一些。我不需要为每个
to_string
实际接受的类型编写一个重载
has to_string\u helper
return
true\u type
?这似乎需要做很多工作!不,它是模板化的。如果
decltype
表达式有效,将使用true大小写,否则将使用false大小写。啊,
int
只是因为您使用函数而不是对象来保存类型,所以您需要重载。有趣的是,我不知道“Fit”的说谎者。它到底是什么?@JonathanMee这是一个用于C++11/14的函数实用程序库。我在回答中链接了它。有趣的是,它与无处不在的Boost相比如何?它在Boost库孵化器中:
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();
}
template<typename T> decltype(to_string(T{})) stringify(T t){
    return to_string(t);
}
template<typename T> enable_if_t<!decltype(to_string(T{})::value, string> (T t){
    return static_cast<ostringstream&>(ostringstream() << t).str();
}
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));
template <typename...>
using void_t = void;
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 { };
namespace detail
{
    template<typename T, REQUIRES(helper::has_to_string(T))>
    std::string stringify(choice<0>, T&& t)
    {
        using std::to_string;
        return to_string(std::forward<T>(t));
    }

    template<std::size_t N>
    std::string stringify(choice<1>, char const(&arr)[N])
    {
        return std::string(arr, N);
    }

    template<typename T, REQUIRES(helper::has_output_operator(T))>
    std::string stringify(choice<2>, T&& t)
    {
        std::ostringstream o;
        o << std::forward<T>(t);
        return std::move(o).str();
    }
}
template<typename T>
auto stringify(T&& t)
    -> decltype( detail::stringify(choice<0>{}, std::forward<T>(t)) )
{
    return detail::stringify(choice<0>{}, std::forward<T>(t));
}
constexpr static std::size_t choice_max = 10;
template<std::size_t N> struct choice : choice<N+1>
{
    static_assert(N < choice_max, "");
};
template<> struct choice<choice_max> {};


#include <type_traits>

template<typename T, typename = void> struct models : std::false_type {};
template<typename MF, typename... Args>
struct models<MF(Args...),
                decltype(MF{}.requires_(std::declval<Args>()...),
                         void())>
    : std::true_type {};

#define REQUIRES(...) std::enable_if_t<models<__VA_ARGS__>::value>* = nullptr
#include <string>
#include <sstream>
namespace helper
{
    using std::to_string;
    struct has_to_string
    {
        template<typename T>
        auto requires_(T&& t) -> decltype( to_string(std::forward<T>(t)) );
    };

    struct has_output_operator
    {
        std::ostream& ostream();

        template<typename T>
        auto requires_(T&& t) -> decltype(ostream() << std::forward<T>(t));
    };
}
template<typename T>
using requires = std::enable_if_t<models<T>::value, int>;

// exemplary application:

template<typename T, requires<helper::has_to_string(T)> = 0>
std::string stringify(choice<0>, T&& t)
{
    using std::to_string;
    return to_string(std::forward<T>(t));
}
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{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
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 >;
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>{});
}
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
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>;
FIT_STATIC_LAMBDA_FUNCTION(stringify) = fit::conditional(
    [](auto x) -> decltype(to_string(x))
    {
        return to_string(x);
    },
    [](auto x) -> decltype(static_cast<ostringstream&>(ostringstream() << x).str())
    {
        return static_cast<ostringstream&>(ostringstream() << x).str();
    }
);
FIT_STATIC_LAMBDA_FUNCTION(stringify) = fit::conditional(
    [](auto x) FIT_RETURNS(to_string(x)),
    [](auto x) FIT_RETURNS(static_cast<ostringstream&>(ostringstream() << x).str())
);
template<typename Callable, typename... Args, typename = decltype(declval<Callable>()(declval<Args>()...))>
std::true_type isCallableImpl(Callable, Args...) { return {}; }

std::false_type isCallableImpl(...) { return {}; }

template<typename... Args, typename Callable>
constexpr bool isCallable(Callable callable) {
    return decltype(isCallableImpl(callable, declval<Args>()...)){};
}
constexpr auto TO_STRING_TEST = [](auto in) -> decltype(std::to_string(in)) { return {}; };
constexpr bool TO_STRING_WORKS = isCallable<Input>(TO_STRING_TEST);