我有以下代码:
#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 BarDefault
和template<typename T> class Bar
这样的单独类的方法?
答案 0 :(得分:4)
Template parameters and template arguments - cppreference.com
非类型模板参数必须具有结构类型,它是以下类型之一(可以选择cv限定,忽略限定符):
lvalue reference type(用于对象或功能);
一个pointer type(用于对象或功能);
一个pointer to member type(指向成员对象或成员函数);
std::nullptr_t; (自C ++ 11起)
具有以下属性的literal类类型:
所有基类和非静态数据成员都是公共的并且是不可变的,并且
所有基类和非静态数据成员的类型是结构类型或其(可能是多维)数组。 (自C ++ 20起)
因此从c ++ 20开始,基本上可以使用自定义结构作为模板值参数。
您可以通过提供模板来解决此问题,该模板将提供默认值:
#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