具有类型模板参数默认值的非类型模板参数

时间:2020-07-08 08:40:30

标签: c++ templates c++14

我有以下代码:

#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<Foo, {}>,则会显示以下错误消息:

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

我已经预料到了。将template<typename T, T DEFAULT> class Bar更改为template<typename T, T DEFAULT = {}> class Bar会导致相同的错误。 同样,模板专业化template<typename T> class Bar<T, {}>出于相同的原因也无法正常工作。

我也尝试尝试std::enable_if_t<std::is_integral<T>::value>,但是找不到可行的解决方案。

有没有可能只编写Bar<Foo>而不必为此编写诸如template<typename T, T DEFAULT> class BarDefaulttemplate<typename T> class Bar这样的单独类的方法?

3 个答案:

答案 0 :(得分:4)

Template parameters and template arguments - cppreference.com

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

因此从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;
}

答案 1 :(得分:0)

您可以设置默认值。

template<class T>
constexpr T brace_init_value{};

然后用作:

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

答案 2 :(得分:0)

感谢@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