【问题标题】:Non-type template parameters, constructor and deduction guide非类型模板参数、构造函数和推导指南
【发布时间】:2019-02-27 07:42:30
【问题描述】:

我想要一个结构模板,它由在构造过程中传递给它的组件的特定值定义,这样不同的值会创建不同的 C++ 数据类型,并且认为非类型模板参数可能对此有用。 像这样(只是一个简单的例子来说明问题,真正的结构会更复杂):

enum ElementType
{
    TYPE1,
    TYPE2
};

template<ElementType elementType, int size>
struct DataType
{
    DataType(ElementType et = elementType, int s = size):
        elementType_(et),
        size_(s)
    {
    }
    ElementType elementType_;
    int size_;
};

int main()
{
    auto d1 = DataType(ElementType::TYPE1, 1);
}

我尝试使用 g++-8 -std=c++17 构建它,它给了我以下错误:

./main.cpp:23:42: error: class template argument deduction failed:
auto d1 = DataType(ElementType::TYPE1, 1);
                                      ^
../main.cpp:23:42: error: no matching function for call to     ‘DataType(ElementType, int)’
../main.cpp:12:2: note: candidate: ‘template<ElementType elementType, int size> DataType(ElementType, int)-> DataType<elementType, size>’
  DataType(ElementType et = elementType, int s = size):
  ^~~~~~~~
../main.cpp:12:2: note:   template argument deduction/substitution failed:
../main.cpp:23:42: note:   couldn't deduce template parameter ‘elementType’
  auto d1 = DataType(ElementType::TYPE1, 1);
                                      ^
../main.cpp:23:42: error: expression list treated as compound expression in functional cast [-fpermissive]
../main.cpp:23:42: warning: left operand of comma operator has no effect [-Wunused-value]

请注意,我不能使用类型模板参数,因为这两种类型的参数是固定的(ElementTypeint),但 DataType(ElementType::TYPE1, 1) 的类型必须与 DataType(ElementType::TYPE1, 2)DataType(ElementType::TYPE1, 1) 的类型不同不同于DataType(ElementType::TYPE2, 1)

【问题讨论】:

  • IIRC 模板扣除是为了扣除类型,而不是为了价值。
  • @Someprogrammerdude 你可以写std::array a{1, 2, 3}; 并推导出数组大小的 value 模板参数。通过显式推导指南,但仍然推演。不是吗?
  • @DanielLangr 那是因为 C++17 std::array deduction guide。没有它就不可能扣除。也许这对于 OP 来说是一个可能的解决方案(除非可以简化代码)?
  • @Someprogrammerdude 这就是我的观点。但是,我认为这不是一个解决方案,因为似乎无法使用引导“函数”参数作为模板参数:X(int n) -&gt; X&lt;n&gt;;- 这不起作用。

标签: c++ templates c++17 template-argument-deduction


【解决方案1】:

你可以这样定义你的模板:

template<ElementType elementType, int size>
struct DataType
{
    const ElementType elementType_ = elementType;
    const int size_ = size;
};

并像这样创建它的一个实例:

auto d1 = DataType<ElementType::TYPE1, 1>();

Demo

【讨论】:

    【解决方案2】:

    要使用推导,传递给构造函数的值需要是一个常量表达式。不幸的是,作为参数传递的值失去了它们的 constexpr 属性。为了防止这种行为,您可以传递包装在类型中的值,例如使用std::integral_constant

    示例用法:

    #include <type_traits>
    
    enum ElementType
    {
        TYPE1,
        TYPE2
    };
    
    template<ElementType elementType, int size>
    struct DataType
    {
        DataType(std::integral_constant<ElementType, elementType>, std::integral_constant<int, size> ic):
            elementType_(elementType),
            size_(ic)
        {
        }
        ElementType elementType_;
        int size_;
    };
    
    int main()
    {
        auto d1 = DataType(std::integral_constant<ElementType, TYPE1>{}, std::integral_constant<int, 1>{});
    }
    

    [live demo]

    为了更方便使用,您可以使用 constexpr 后缀运算符环绕整型 const:

    #include <type_traits>
    
    enum ElementType
    {
        TYPE1,
        TYPE2
    };
    
    
    template <class... Ts>
    constexpr int ival(Ts... Vs) {
        char vals[sizeof...(Vs)] = {Vs...};
        int result = 0;
        for (int i = 0; i < sizeof...(Vs); i++) {
            result *= 10;
            result += vals[i] - '0';
        }
        return result;
    }
    
    template <class T, class... Ts>
    constexpr ElementType etval(T V, Ts... Vs) {
        if (V == '1')
           return TYPE1;
        if (V == '2')
           return TYPE2;
    }
    
    template <char... Vs>
    std::integral_constant<int, ival(Vs...)> operator""_i() {
        return {};
    }
    
    template <char... Vs>
    std::integral_constant<ElementType, etval(Vs...)> operator""_et() {
        return {};
    }
    
    
    template<ElementType elementType, int size>
    struct DataType
    {
        DataType(std::integral_constant<ElementType, elementType>, std::integral_constant<int, size> ic):
            elementType_(elementType),
            size_(ic)
        {
        }
        ElementType elementType_;
        int size_;
    };
    
    int main()
    {
        auto d1 = DataType(1_et, 1_i);
    }
    

    [live demo]

    【讨论】:

    • 即使这样可行,我也没有看到将值直接作为模板参数传递的任何好处。这只是额外的样板和更多的输入?
    猜你喜欢
    • 2022-12-22
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多