首页 > 解决方案 > 如何检查是否可以从给定类型构造模板类型

问题描述

std::enable_if_t如果模板参数是任何类型的std::string(显示如何使用来自多个库的通用字符串执行此操作的答案的奖励积分),我正在尝试使用它来启用一个类。

我不想做“if Textis a const char*, std::string, std::string_view, etc.” 本质上,我不想具体提及每一个可能的类似字符串的对象。我要对这个对象做的就是将它打印到控制台,并且该Text对象将被存储为一个类属性。

有没有比考虑每种类型更优雅的方法呢?

标签: c++classtemplatestemplate-meta-programming

解决方案


您可以为此使用is_detected。我们正在尝试检查给定类型是否可以使用std::couts 的重载之一进行打印,operator<<或者该类型本身是否提供operator<<用于打印到std::cout. 有关我如何实现此功能的更一般说明,请查看https://www.fluentcpp.com/2017/06/02/write-template-metaprogramming-expressively/

首先,我们is_detected为 std::cout 本身的重载定义一个合适的:

// check std::cout.operator<<(T {})
template<typename = void, typename Arg = void> struct test_operator_of_cout : std::false_type {};

template<typename Arg>
struct test_operator_of_cout<std::void_t<decltype(std::cout.operator<<(std::declval<Arg>()))>, Arg>
    : std::true_type {};

template<typename Arg>
constexpr bool test_operator_of_cout_v = test_operator_of_cout<void, Arg>::value;

另一个用于所有重载的operator<<(ostream&, T {}). 我在上面发布的链接对此进行了概括,以减少代码冗余。

// check operator<<(std::cout, T {})
template<typename = void, typename Arg = void> struct test_operator_of_struct : std::false_type {};

template<typename Arg>
struct test_operator_of_struct<std::void_t<decltype(operator<<(std::cout, std::declval<Arg>()))>, Arg>
    : std::true_type {};

template<typename Arg>
constexpr bool test_operator_of_struct_v = test_operator_of_struct<void, Arg>::value;

我们现在可以使用这些类型特征通过 enable_if 实现打印功能:

template<typename T> struct MyClass {

    T t;

    template<
        typename Consider = T,
        typename = std::enable_if_t<
            ( test_operator_of_cout_v<Consider> || test_operator_of_struct_v<Consider>) 
            && !std::is_arithmetic_v<Consider>
        >
    > void print() {
        std::cout << t;
    }

};

这里有两点需要注意:

  • 您需要Consider = T. 否则,编译器将尝试实例化函数的声明,这对于不满足条件的类型来说是错误的。查看此 SO-Answer 以获得更深入的解释:std::enable_if 有条件地编译成员函数
  • 由于!std::is_arithmetic. 我个人不会包括这个,因为我看不出我的班级不应该允许完全可打印的类型可打印的原因。

现在我们可以看看什么是可打印的,什么是不可打印的:

struct NotPrintable {};

struct Printable {
    friend std::ostream& operator<<(std::ostream& os, const Printable& p) {
         return os;
    }
};

auto foo() {

    MyClass<const char *> chars;
    chars.print(); //compiles

    MyClass<std::string> strings;
    strings.print(); //compiles

    MyClass<std::string_view> string_views;
    string_views.print(); //compiles

    MyClass<Printable> printables;
    printables.print(); // compiles

    // MyClass<int> ints;
    // ints.print(); // Does not compile due to !is_arithmetiv_v

    // MyClass<NotPrintable> not_printable;
    // not_printable.print(); //Does not compile due to operator checking

}

您可以在此处查看完整示例:https ://godbolt.org/z/ZC9__e


推荐阅读