使用 SFINAE 的 Variadic 变量模板专业化
Variadic Variable Template specialization with SFINAE
我一直在尝试新的(ish)C++14 变量模板功能,并且 运行 在编译过程中遇到这个奇怪的错误(g++ 6.3.0,但也使用 8.1.0 进行了测试)
templated_variables.cpp:32:19: error: wrong number of template arguments (1, should be at least 1)
const std::string config_data<WantName> = "Name: grep";
还有更多错误,但都是同一类错误。代码如下
#include <type_traits>
#include <string>
#include <iostream>
struct Want {};
struct WantName : Want {};
struct WantDir : Want {};
template<bool... values>
struct all_of : std::true_type {};
template<bool... values>
struct all_of<true, values...> : all_of<values...> {};
template<bool... values>
struct all_of<false, values...> : std::false_type {};
template <
typename Tag, typename ... Tags,
typename =
typename std::enable_if<
all_of<
std::is_base_of<Want, Tag>::value,
std::is_base_of<Want, Tags>::value...
>::value
>::type
>
const std::string config_data = config_data<Tag> + '\n' + config_data<Tags...>;
template <>
const std::string config_data<WantName> = "Name: grep";
template <>
const std::string config_data<WantDir> = "Directory: /usr/bin/";
int main() {
std::cout << config_data<WantDir, WantName> << '\n';
std::cout << config_data<WantDir> << '\n';
std::cout << config_data<WantName> << '\n';
}
这里的问题似乎是 SFINAE 风格 std::enable_if
,因为如果我删除它,编译就没有问题。但奇怪的是,如果我用 config_data<Want*, Want>
删除 config_data<Want*>
的每个实例(或其他一些以 Want
为基础的 class ,我们也不会出现编译错误。
我的问题是,我怎样才能避免
(a) 失去阻止此模板用户传入 运行dom 类型作为模板参数的能力,或
(b) 要求在变量模板的每个实例化中使用不必要的基本参数。
我意识到在这个(人为的)示例中,(a) 不是一个合理的问题。任何具有未实现其中一种特化的类型的模板实例化都将无法编译。但这在一般情况下肯定会是一个问题,它仍然没有解释为什么使用有效的第一个参数、空参数包和空白默认参数实例化模板会导致编译错误。
让我们暂时放下实际的默认模板参数,并为最后一个参数命名。
您的主要变量模板如下所示:
template <typename Tag, typename... Tags, typename _Unnamed>
const std::string config_data = ...;
您的第一个专业是:
template <>
const std::string config_data<WantName> = ...;
所以在这个专业化中,Tag=WantName
、Tags={}
和 _Unnamed
是……到底是什么?它没有指定。事实上,没有办法实际指定它。有一个尾随的默认模板参数很好,它总是会被默认。但是一旦你试图将它专门化,就不可能做到了。
在 C++20 中,您将能够适当地约束它:
template <DerivedFrom<Want> Tag, DerivedFrom<Want>... Tags>
const std::string config_data = ...;
在那之前,你真的需要SFINAE吗?不完全确定你从中得到了什么。如果你只是放下它,一切正常。
我一直在尝试新的(ish)C++14 变量模板功能,并且 运行 在编译过程中遇到这个奇怪的错误(g++ 6.3.0,但也使用 8.1.0 进行了测试)
templated_variables.cpp:32:19: error: wrong number of template arguments (1, should be at least 1)
const std::string config_data<WantName> = "Name: grep";
还有更多错误,但都是同一类错误。代码如下
#include <type_traits>
#include <string>
#include <iostream>
struct Want {};
struct WantName : Want {};
struct WantDir : Want {};
template<bool... values>
struct all_of : std::true_type {};
template<bool... values>
struct all_of<true, values...> : all_of<values...> {};
template<bool... values>
struct all_of<false, values...> : std::false_type {};
template <
typename Tag, typename ... Tags,
typename =
typename std::enable_if<
all_of<
std::is_base_of<Want, Tag>::value,
std::is_base_of<Want, Tags>::value...
>::value
>::type
>
const std::string config_data = config_data<Tag> + '\n' + config_data<Tags...>;
template <>
const std::string config_data<WantName> = "Name: grep";
template <>
const std::string config_data<WantDir> = "Directory: /usr/bin/";
int main() {
std::cout << config_data<WantDir, WantName> << '\n';
std::cout << config_data<WantDir> << '\n';
std::cout << config_data<WantName> << '\n';
}
这里的问题似乎是 SFINAE 风格 std::enable_if
,因为如果我删除它,编译就没有问题。但奇怪的是,如果我用 config_data<Want*, Want>
删除 config_data<Want*>
的每个实例(或其他一些以 Want
为基础的 class ,我们也不会出现编译错误。
我的问题是,我怎样才能避免
(a) 失去阻止此模板用户传入 运行dom 类型作为模板参数的能力,或
(b) 要求在变量模板的每个实例化中使用不必要的基本参数。
我意识到在这个(人为的)示例中,(a) 不是一个合理的问题。任何具有未实现其中一种特化的类型的模板实例化都将无法编译。但这在一般情况下肯定会是一个问题,它仍然没有解释为什么使用有效的第一个参数、空参数包和空白默认参数实例化模板会导致编译错误。
让我们暂时放下实际的默认模板参数,并为最后一个参数命名。
您的主要变量模板如下所示:
template <typename Tag, typename... Tags, typename _Unnamed>
const std::string config_data = ...;
您的第一个专业是:
template <>
const std::string config_data<WantName> = ...;
所以在这个专业化中,Tag=WantName
、Tags={}
和 _Unnamed
是……到底是什么?它没有指定。事实上,没有办法实际指定它。有一个尾随的默认模板参数很好,它总是会被默认。但是一旦你试图将它专门化,就不可能做到了。
在 C++20 中,您将能够适当地约束它:
template <DerivedFrom<Want> Tag, DerivedFrom<Want>... Tags>
const std::string config_data = ...;
在那之前,你真的需要SFINAE吗?不完全确定你从中得到了什么。如果你只是放下它,一切正常。