Как условно кодировать шаблонную функцию / класс, основываясь на особенностях реализации? - PullRequest
0 голосов
/ 23 октября 2019

Пример, который я привожу, является искусственным и бессмысленным, но это сокращение более сложного (и более логичного) кода.

Код

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
}

1 Ответ

2 голосов
/ 23 октября 2019

Да. Сначала нам нужна черта, чтобы проверить, является ли данный тип пригодным для потока:

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

Вот и все. Хорошей практикой является сокращение сокращенного значения:

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