具有模板类默认参数的 C++17 别名模板
C++17 alias template with template class default arguments
似乎C++17 添加了在所有参数都有默认值时删除模板类上的"<>"的功能(就像我们已经能够长时间使用函数一样(,例如:
template<int LENGTH = 1>
struct MyStruct{ int arr[LENGTH]; };
int main()
{
MyStruct<2> a;
MyStruct<> b; // old way to use defaults
MyStruct c; // new way to use defaults
return 0;
}
但是,在使用别名模板时,该功能似乎不再有效,例如:
template<int LENGTH = 1>
struct MyStruct{ int arr[LENGTH]; };
template<int LENGTH = 1>
using MyAlias = MyStruct<LENGTH>;
int main()
{
MyAlias<2> a;
MyAlias<> b; // old way still works
MyAlias c; // new way doesn't compile:
// gcc 7.3: missing template arguments before 'c'
// clang 6.0.0: declaration of variable 'c' with deduced type 'MyAlias' requires an initializer
return 0;
}
这对我来说似乎是意想不到的行为。是否有任何解决方法仍允许删除"<>"?(我知道可以用不同的名称创建一个单独的typedef,例如:使用MyAlias2 = MyStruct<>,但我想要相同的确切名称。我也知道定义可以欺骗它,例如 #define MyAlias MyStruct,但假设这只是最后的手段。
这对我来说似乎是意想不到的行为。是否有任何解决方法仍允许删除"<>"?
这是预期行为。好吧,取决于你对我的期望。类模板参数推导仅适用于使用未指定任何模板参数的主类模板名称的上下文,并且仅适用于创建对象的上下文。
它不适用于别名模板的上下文(如 OP 所示(。它不适用于函数模板推导的上下文。
除非有人提议改变这一点,否则解决方法是只写MyAlias<>
。
有一个建议来概括using
声明,以便假设您可以编写using MyAlias = MyStruct;
并将其作为别名模板。在这种情况下,允许MyAlias c;
似乎是合理的,因为MyAlias
直接命名类模板。
但一般问题更复杂,因为别名模板可以执行诸如重新排序类型或添加新类型之类的操作。您必须回答以下问题:
template <typename T> using tuple_int = std::tuple<T, int>;
tuple_int t(4, 2); // would this work? how?
tuple_int u(4, '2'); // what about this?
我不是说没有答案。我只是说这不是一件小事。
是否有任何解决方法仍允许删除"<>"?
可能的解决方法可能是透明继承:
template<int LENGTH = 1>
struct MyStruct{ int arr[LENGTH]; };
template<int LENGTH = 1>
struct MyAlias : MyStruct<LENGTH> { };
int main()
{
MyAlias<2> a;
MyAlias<> b;
MyAlias c;
return 0;
}
然而,一个(可能(危险的副作用是基类没有虚拟析构函数,如果多态使用,可能会导致内存泄漏。
这对我来说似乎是意想不到的行为。
类模板参数推导(启用您尝试使用的功能(似乎需要实际类模板的名称,而不是模板别名的名称。编译器基本上所做的类似于转换
MyStruct obj;
自
template <int LENGTH=1>
MyStruct<LENGTH> f() { return MyStruct<Length>{ }; }
auto obj = f();
但是,对于别名,您可以执行以下操作:
template <int LENGTH = 1>
using MyAlias = MyStruct<LENGTH + 1>;
如果上面的转换只是用MyStruct
替换名称MyAlias
,则完全会错过"+ 1",这意味着这个问题没有微不足道的解决方案 - 但到这个时候,标准中没有任何地方处理这种情况,所以可以理解它不会编译。
我不推荐这种方法,但我找到了一种即使您没有 C++17 也能工作的方法。如果您只想进行简单的文本替换并且您有 C++17,您可以使用宏(如问题中所述(。否则,如果您不介意略有不同的语法,则可以这样做:
template<int LENGTH = 1>
struct MyStruct{ int arr[LENGTH]; };
#define MyAlias(...) MyStruct<__VA_ARGS__>
using MyAlias = MyStruct<>;
int main()
{
MyAlias(2) a; // MyAlias<2>
MyAlias() b; // MyAlias<>
MyAlias c;
}
与其他方法相比,有几个优点:
- 你不需要C++17
- 您不必重新指定默认值 (DRY 原则(
- 您不需要任何额外的类、函数等。
- 它可以与别名模板专用化的解决方法结合使用
例如,假设您要创建一个类来模拟各种大小的浮点数。如果大小与内置类型匹配,则希望直接使用它来提高效率,否则使用类。此外,您希望大小默认为平台上最有效的大小,以减少详细程度。
理想情况下,我希望能够使用别名模板,例如:
template<int BITS>
struct BasicReal final
{
constexpr BasicReal(const double){};
};
template<int BITS = 64>
using Real = BasicReal<BITS>;
template<> // Alias template specialization if it worked
using Real<64> = double;
template<> // Alias template specialization if it worked
using Real<32> = float;
int main()
{
Real r = 1.2; // Alias template argument deduction if it worked
Real<16> r16 = 1.2;
Real<32> r32 = 1.2;
Real<64> r64 = 1.2;
return r;
}
但是,就目前而言,我可以使用以下解决方法(即使在 C++11 中(:
template<int BITS>
struct BasicReal final
{
constexpr BasicReal(const double){};
};
template<int BITS = 64>
struct RealAlias
{
using Type = BasicReal<BITS>;
};
template<>
struct RealAlias<64>
{
using Type = double;
};
template<>
struct RealAlias<32>
{
using Type = float;
};
#define Real(...) RealAlias<__VA_ARGS__>::Type
using Real = RealAlias<>::Type;
int main()
{
Real r = 1.2;
Real(16) r16 = 1.2;
Real(32) r32 = 1.2;
Real(64) r64 = 1.2;
return r;
}
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 部分定义/别名模板模板参数
- 如何在c++17中制作一个模板包装器/装饰器
- 如何在C++20中创建模板别名的推导指南
- 枚举环境变量的惯用C++14/C++17方法
- gcc和c++17的过载解析失败
- 数据成员SFINAE的C++17测试:gcc vs clang
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- C++17中的并行执行策略
- C++17中函数模板中的静态数组初始化(MSVC 2019)
- 并行用于C++17中数组索引范围内的循环
- 告诉c++编译器该参数没有别名
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 在C++17中,引用const字符串的语义应该是什么
- 为什么这种直接初始化有效?(C++17)
- boost::spirit::karma 替代生成器,带有 boost::variant 由字符串和字符串别名组成
- 继承模板类中的类型别名
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- 具有模板类默认参数的 C++17 别名模板
- 根据模板参数条件 C++17 创建成员别名