如何根据实例化特征有条件地编码模板 function/class?

How to conditionally code a template function/class based on instantiation traits?

我提供的示例是虚假且毫无意义的,但它是对更复杂(和更逻辑)代码的简化。

代码

template<typename T>
void MyTemplate(T t) 
{
    wstringstream stringStream;
    stringStream << t << endl;

    // rest of the code
}

struct MyClass {};

void func()
{
    MyTemplate(1); // OK
    MyTemplate(MyClass()); // error C2679: binary '<<': no operator found which takes a right-hand operand of type 'T' (or there is no acceptable conversion)
}

问题

MyTemplate 是通用的,但不能用任何实例化进行编译。

问题

有没有办法按以下方式对 MyTemplate 进行编码:

void MyTemplate(T t) 
{
    wstringstream stringStream;

    // if/directives/magic to check if object is stream-able do
        stringStream << t << endl;
    // else
        stringStream << "object is not stream-able" << endl;

    // rest of the code
}

是的。 首先,我们需要一个特征来检查给定类型是否可流式传输到给定流:

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

template<typename T, typename Stream>
struct can_stream<T, Stream, std::void_t<decltype(std::declval<Stream>() << std::declval<T>())>> : std::true_type{};

在我们的工具箱中有了这个,我们可以在我们的函数中使用 if constexpr:

template<typename T>
void MyTemplate(T t) 
{
    wstringstream stringStream;

    if constexpr(can_stream<T, std::wstringstream>::value)
    { 
        stringStream << t << endl;
    }
    else
    {
        stringStream << "object is not stream-able" << endl;
    }
}

就是这样。 为特征值提供 shorthand 是一种很好的做法:

template<typename T, typename Stream>
inline constexpr auto can_stream_v = can_stream<T, Stream>::value;

所以我们有了最终的工作示例:

#include <iostream>
#include <sstream>
#include <type_traits>

using namespace std;

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

template<typename T, typename Stream>
struct can_stream<T, Stream, std::void_t<decltype(std::declval<Stream>() << std::declval<T>())>> : std::true_type{};

template<typename T, typename Stream>
inline constexpr auto can_stream_v = can_stream<T, Stream>::value;

template<typename T>
void MyTemplate(T t) 
{
    wstringstream stringStream;

    if constexpr(can_stream_v<T, std::wstringstream>)
    { 
        stringStream << t << endl;
    }
    else
    {
        stringStream << "object is not stream-able" << endl;
    }

    std::wcout << stringStream.str();
}

struct MyClass {};

int main()
{
    MyTemplate(1);
    MyTemplate(MyClass()); 
    return 0;
}