【问题标题】:non-type template parameter of user-defined type用户定义类型的非类型模板参数
【发布时间】:2014-08-28 13:31:08
【问题描述】:

我正在尝试定义具有用户定义类型的非类型模板参数的模板类。不幸的是,到目前为止还没有成功。真正的代码有点太长了,但是一个简化的例子是这样的:

#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 就足够了。还是真的只能是内置的整数类型之一?

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

【问题讨论】:

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


    【解决方案1】:

    不,你不能。

    n3376 14.1/7

    不应将非类型模板参数声明为具有浮动 点、类或空类型。

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

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

    live example

    【讨论】:

    • 感谢您澄清这一点。太糟糕了,这是不可能的。
    【解决方案2】:

    这整个问题正是有理数被实现为模板类的原因,模板参数中有实际数字。要获得一个不仅存储 int 的模板参数,您必须创建一个与 std::ratio 类似的类,该类也仅在编译时进行评估。

    至于您的实际示例,请考虑编写类似以下内容:

    template<class T, T ... Params>
    class Maybe {
    

    然后

    Test<Maybe<int,5> > is_set;
    

    Test<Maybe<int> > not_set;
    

    【讨论】:

    • 这是一个非常好的可变参数模板应用。我正在考虑template&lt;class T, bool is=false, T value={}&gt; class Maybe{}; 形式的东西。但你的方法要好得多。
    【解决方案3】:

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

    在 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&lt;int, 3&gt;().value

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

    这样的话,再用上面的逻辑就好了!

    【讨论】:

      【解决方案4】:

      在 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."
      }
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-28
        • 1970-01-01
        • 1970-01-01
        • 2019-04-24
        • 2021-03-18
        • 1970-01-01
        • 1970-01-01
        • 2011-08-06
        相关资源
        最近更新 更多