【问题标题】:Non-type template parameter with default value of type template parameter具有类型模板参数默认值的非类型模板参数
【发布时间】:2020-07-08 08:40:30
【问题描述】:

我有以下代码:

#include <iostream>
#include <string>
#include <type_traits>

struct Foo
{
   int i;
   int j;
};

template<typename T, T DEFAULT>
class Bar
{
public:
    Bar(): mVal(DEFAULT)
    {
        std::cout << "Bar constructor with mVal = " << mVal << "\n";
    }
    
    ~Bar(){}
    
    Bar(const T &i) : mVal(i)
    {
        std::cout << "Bar constructor with mVal = " << mVal << "\n";
    }
    
    Bar &operator=(T const &val)
    {
        mVal = val;
        std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
        return *this;
    }
    
    explicit operator T() const
    {
        return mVal;
    }

private:
    T mVal;
};



int main()
{
  std::cout << "Hello \n";
  
  Bar<int, 10> bar1;
}

只要Bar 中的第一个模板参数是整数类型,这在 gcc C++14 中就可以正常工作。如果我想做Bar&lt;Foo, {}&gt;,则会打印以下错误消息:

on-type template parameters of class type only available with '-std=c++2a' or '-std=gnu++2a'

我已经预料到了。将 template&lt;typename T, T DEFAULT&gt; class Bar 更改为 template&lt;typename T, T DEFAULT = {}&gt; class Bar 会导致相同的错误。 同样的原因,模板专业化 template&lt;typename T&gt; class Bar&lt;T, {}&gt; 也不起作用。

我也尝试过使用std::enable_if_t&lt;std::is_integral&lt;T&gt;::value&gt;,但找不到可行的解决方案。

有没有什么方法可以只写Bar&lt;Foo&gt; 而不必为它写一个像template&lt;typename T, T DEFAULT&gt; class BarDefaulttemplate&lt;typename T&gt; class Bar 这样的单独的类?

【问题讨论】:

    标签: c++ templates c++14


    【解决方案1】:

    Template parameters and template arguments - cppreference.com

    一个非类型模板参数必须有一个结构类型,它是以下类型之一(可选cv-qualified,限定符被忽略):

    所以基本上自定义结构作为模板值参数从 c++20 开始就可以使用了。

    Demo

    您可以通过提供依赖的模板来解决这个问题:哪个作业要提供默认值:

    https://godbolt.org/z/RFp_xH

    #include <iostream>
    #include <string>
    #include <type_traits>
    
    struct Foo
    {
       int i = 42;
       int j = 4;
    };
    
    std::ostream& operator<<(std::ostream& out, const Foo& a)
    {
        return out << a.i << ',' << a.j;
    }
    
    template<typename T>
    struct BarDefaultValue
    {
        constexpr static T value()
        {
            return T{};
        }
    };
    
    template<>
    struct BarDefaultValue<int>
    {
        constexpr static int value()
        {
            return 42;
        }
    };
    
    template<typename T, typename D = BarDefaultValue<T>>
    class Bar
    {
    public:
        Bar(): mVal(D::value())
        {
            std::cout << "Bar constructor with mVal = " << mVal << "\n";
        }
        
        ~Bar(){}
        
        Bar(const T &i) : mVal(i)
        {
            std::cout << "Bar constructor with mVal = " << mVal << "\n";
        }
        
        Bar &operator=(T const &val)
        {
            mVal = val;
            std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
            return *this;
        }
        
        explicit operator T() const
        {
            return mVal;
        }
    
    private:
        T mVal;
    };
    
    int main()
    {
      std::cout << "Hello \n";
      
      Bar<int> bar1;
      Bar<Foo> bar2;
    }
    

    【讨论】:

      【解决方案2】:

      你可以设置一个默认值来提供。

      template<class T>
      constexpr T brace_init_value{};
      

      然后用作:

      template<typename T, T DEFAULT = brace_init_value<T> >
      class Bar
      {
      

      【讨论】:

      • 感谢您的回答,但不幸的是,这不是在 C++14 中编译的
      • @EmbedEngineer 为什么不呢?什么是编译器和完整的错误?
      • 错误保持不变 on-type template parameters of class type only available with '-std=c++2a' or '-std=gnu++2a 编译器版本是 gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008 带有“-std=gnu++14”标志
      【解决方案3】:

      感谢@Marek R 的想法。

      我对这个问题的解决方案是使用 C++14 中的 gcc 编译的以下代码:

      #include <iostream>
      #include <string>
      #include <type_traits>
      
      template <typename T>
      constexpr typename std::enable_if<std::is_class<T>::value, T>::type BarDefaultValue(const int &)
      {
          return {};
      }
      
      template <typename T>
      constexpr typename std::enable_if<std::is_integral<T>::value, T>::type BarDefaultValue(const int &ret)
      {
          return static_cast<T>(ret);
      }
      
      struct Foo
      {
         int i;
         int j;
      };
      
      std::ostream& operator<<(std::ostream& out, const Foo& a)
      {
          return out << a.i << ',' << a.j;
      }
      
      template<typename T, int DEFAULT = 0>
      class Bar
      {
      public:
          Bar(): mVal(BarDefaultValue<T>(DEFAULT))
          {
              std::cout << "Bar constructor with mVal = " << mVal << "\n";
          }
          
          ~Bar(){}
          
          Bar(const T &i) : mVal(i)
          {
              std::cout << "Bar constructor with mVal = " << mVal << "\n";
          }
          
          Bar &operator=(T const &val)
          {
              mVal = val;
              std::cout << "Bar assignment operator with mVal = " << mVal << "\n";
              return *this;
          }
          
          explicit operator T() const
          {
              return mVal;
          }
      
      private:
          T mVal;
      };
      
      
      
      int main()
      {
        std::cout << "Hello \n";
        
        Bar<int, 10> bar1;
        Bar<Foo> bar2;
      }
      

      这个文件的输出是:

      Hello 
      Bar constructor with mVal = 10
      Bar constructor with mVal = 0,0
      

      【讨论】:

        猜你喜欢
        • 2019-01-09
        • 1970-01-01
        • 1970-01-01
        • 2021-03-18
        • 1970-01-01
        • 1970-01-01
        • 2023-01-25
        • 2014-09-16
        • 1970-01-01
        相关资源
        最近更新 更多