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它用于阻止用户提供额外的模板参数。请参阅。@JonathanMeeMF
不是函数类型。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。其他人注意到了这种相似性,并在之前给我看了一个链接。只是我在写这篇文章时感到累了evoid\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
returntrue\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);