带有模板 class 个默认参数的 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,例如:using MyAlias2 = MyStruct<>,但我想要完全相同的名称。我也知道定义可以欺骗它,例如#define MyAlias MyStruct,但假设那会只能是最后的手段。)
That seems like unexpected behavior to me. Are there any workarounds to still allow the "<>" to be dropped?
这是预期的行为。好吧,我猜取决于你的期望。 Class 模板参数推导 only 适用于使用未指定任何模板参数的主要 class 模板名称的上下文,并且 only 在创建对象的上下文中。
它不适用于别名模板的上下文(如 OP 中所示)。并且不适用于函数模板推导的上下文。
除非有人提议更改此设置,否则解决方法是只写 MyAlias<>
。
有一个通用化 using
声明的提议,因此假设您可以编写 using MyAlias = MyStruct;
并将其作为别名模板。在这种情况下,允许 MyAlias c;
似乎是合理的,因为 MyAlias
直接命名了一个 class 模板。
但一般问题更复杂,因为别名模板可以执行诸如重新排序类型或添加新类型之类的操作。您必须回答以下问题:
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?
我不是说没有答案。我只是说这不是一件小事。
Are there any workarounds to still allow the "<>" to be dropped?
一种可能的解决方法是透明继承:
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;
}
然而,一个(可能)危险的副作用是基 class 没有虚拟析构函数,如果以多态方式使用,可能会导致内存泄漏。
That seems like unexpected behavior to me.
Class template argument deduction 启用了您尝试使用的功能,似乎需要一个真正的 class 模板名称,而不是模板别名。编译器基本上做的是像转换
MyStruct obj;
到
template <int LENGTH=1>
MyStruct<LENGTH> f() { return MyStruct<Length>{ }; }
auto obj = f();
但是,对于别名,您可以这样做:
template <int LENGTH = 1>
using MyAlias = MyStruct<LENGTH + 1>;
如果上面的转换只是将名称 MyAlias
替换为 MyStruct
,那么上面的转换将完全错过“+ 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
原理)
- 您不需要任何额外的classes、函数等
- 它可以与别名模板专业化的解决方法结合使用
例如,假设您想制作一个 class 来模拟各种大小的浮点数。如果大小与内置类型相匹配,你想直接使用它以提高效率,否则你使用你的 class。此外,您希望大小默认为平台上最有效的大小以减少冗长。
理想情况下,我希望能够使用像这样的别名模板:
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 添加了在模板 类 上删除“<>”的功能,当所有参数都有默认值时(就像我们长期以来对函数所做的那样)时间)例如:
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,例如:using MyAlias2 = MyStruct<>,但我想要完全相同的名称。我也知道定义可以欺骗它,例如#define MyAlias MyStruct,但假设那会只能是最后的手段。)
That seems like unexpected behavior to me. Are there any workarounds to still allow the "<>" to be dropped?
这是预期的行为。好吧,我猜取决于你的期望。 Class 模板参数推导 only 适用于使用未指定任何模板参数的主要 class 模板名称的上下文,并且 only 在创建对象的上下文中。
它不适用于别名模板的上下文(如 OP 中所示)。并且不适用于函数模板推导的上下文。
除非有人提议更改此设置,否则解决方法是只写 MyAlias<>
。
有一个通用化 using
声明的提议,因此假设您可以编写 using MyAlias = MyStruct;
并将其作为别名模板。在这种情况下,允许 MyAlias c;
似乎是合理的,因为 MyAlias
直接命名了一个 class 模板。
但一般问题更复杂,因为别名模板可以执行诸如重新排序类型或添加新类型之类的操作。您必须回答以下问题:
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?
我不是说没有答案。我只是说这不是一件小事。
Are there any workarounds to still allow the "<>" to be dropped?
一种可能的解决方法是透明继承:
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;
}
然而,一个(可能)危险的副作用是基 class 没有虚拟析构函数,如果以多态方式使用,可能会导致内存泄漏。
That seems like unexpected behavior to me.
Class template argument deduction 启用了您尝试使用的功能,似乎需要一个真正的 class 模板名称,而不是模板别名。编译器基本上做的是像转换
MyStruct obj;
到
template <int LENGTH=1>
MyStruct<LENGTH> f() { return MyStruct<Length>{ }; }
auto obj = f();
但是,对于别名,您可以这样做:
template <int LENGTH = 1>
using MyAlias = MyStruct<LENGTH + 1>;
如果上面的转换只是将名称 MyAlias
替换为 MyStruct
,那么上面的转换将完全错过“+ 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 原理)
- 您不需要任何额外的classes、函数等
- 它可以与别名模板专业化的解决方法结合使用
例如,假设您想制作一个 class 来模拟各种大小的浮点数。如果大小与内置类型相匹配,你想直接使用它以提高效率,否则你使用你的 class。此外,您希望大小默认为平台上最有效的大小以减少冗长。
理想情况下,我希望能够使用像这样的别名模板:
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;
}