用户定义类型的非类型模板参数

时间:2014-08-28 13:31:08

标签: c++ templates c++11 constexpr

我正在尝试定义一个模板类,该模板类具有用户定义类型的非类型模板参数。不幸的是,迄今没有成功。真正的代码有点过于冗长,但简化示例如下所示:

#include <iostream>

template <class T>
class Maybe {
    bool is_ = false;
    T value_;

  public:
    constexpr Maybe() = default;
    constexpr Maybe(T value) : is_(true), value_(value) {}

    constexpr bool is() const { return is_; }
};

template <Maybe<int> parm>
struct Test {
    void say() const {
        std::cout << "parm is " << (parm.is() ? "set" : "not set") << ".\n";
    }
};

int main() {
    Test<Maybe<int>{}> not_set;
    Test<Maybe<int>(2)> is_set;

    not_set.say();
    is_set.say();
}

当我尝试编译此代码时(使用Clang 3.4),我收到以下错误消息:

test.cc:15:22: error: a non-type template parameter cannot have type
      'Maybe<int>'
template <Maybe<int> parm>
                     ^
test.cc:23:10: error: value of type 'Maybe<int>' is not implicitly
      convertible to 'int'
    Test<Maybe<int>{}> not_set;
         ^~~~~~~~~~~~
test.cc:24:10: error: value of type 'Maybe<int>' is not implicitly
      convertible to 'int'
    Test<Maybe<int>(2)> is_set;
         ^~~~~~~~~~~~~
3 errors generated.

现在,我知道非类型模板参数必须满足some conditions。但是,我认为constexpr就足够了。或者它真的只能是内置的整体类型之一吗?

有没有办法传递我自己的用户定义类型的非类型模板参数?

4 个答案:

答案 0 :(得分:6)

不,你不能。

n3376 14.1 / 7

  

非类型模板参数不得声明为浮动   点,类或空类型。

template<double d> class X; // error
template<double* pd> class Y; // OK
template<double& rd> class Z; // OK

所以,你可以传递指针或引用,但不能传递类类型的对象。

live example

答案 1 :(得分:2)

这整个问题是为什么有理数被实现为模板类以及模板参数中的实际数字的原因。要获得一个模板参数存储不仅仅是一个int,你必须创建一个类似于std::ratio的类,它只在编译时进行评估。

至于你的实际例子,考虑写一些类似于:

的东西
template<class T, T ... Params>
class Maybe {

然后

Test<Maybe<int,5> > is_set;

Test<Maybe<int> > not_set;

答案 2 :(得分:1)

我知道这个问题很老,但我想指出一个模板元编程方法。

在c ++中你可以将任何类型传递给这样的模板,那么为什么不把它变成一个类型?

为了做到这一点,你需要为Maybe类创建一个包装类:

template <typename T>
struct maybe_wrap 
{
    Maybe<T> value{};
}

template<typename T, T value>
struct maybe_wrap
{
    Maybe<T> value{value};
}

然后只需将maybe_wrap作为类型名称传递,只需在需要时访问maybe_wrap<int, 3>().value即可!

唯一的限制是T可能不是非类型值之一(int,bool等)。

在这种情况下,再次使用上述逻辑!

答案 3 :(得分:1)

在C ++ 20中,可以使用 structural 文字类类型:

#include <iostream>

struct NullOptT {} NullOpt;

/**
 * Literal class type.
 *
 * Represents an optionally provided `int`.
 */
struct OptionalInt {
    constexpr OptionalInt(NullOptT) {}
    constexpr OptionalInt(int value): has_value(true), value(value) {}

    const bool has_value = false;
    const uint32_t value {};
};

/**
 * Prints whether or not a value was provided for "maybe" WITHOUT branching :)
 */
template<OptionalInt maybe>
void Print() {
    if constexpr(maybe.has_value) {
        std::cout << "Value is: " << maybe.value << std::endl;
    } else {
        std::cout << "No value." << std::endl;
    }
}

// Note: implicit conversions are at play!
int main()
{
    Print<123>();     // Prints "Value is: 123"
    Print<NullOpt>(); // Prints "No value."
}

我在Literal Classes as Non-type Template Parameters in C++20上写了一篇博文,详细介绍了类文字NTTP的用法和限制。