使用SFINAE

时间:2018-08-02 22:33:44

标签: c++ c++14 variadic-templates sfinae variable-templates

我一直在尝试新的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*>删除config_data<Want*, Want>的每个实例(或其他以Want为基数的类,我们也不会出现编译错误。

我的问题是,我该如何避免两者之一

(a)无法阻止此模板的用户传入随机类型作为模板参数,或者

(b)在变量模板的每个实例中都要求使用不必要的基本参数。

我意识到,在这个(人为)示例中,(a)不是合理的问题。如果模板实例化的类型没有实现其中一种专业化,则将无法编译。但这当然在一般情况下可能是一个问题,并且仍然无法解释为什么用有效的第一个参数,空的参数包和空白的默认参数实例化模板会导致编译错误。

1 个答案:

答案 0 :(得分:1)

让我们将实际的默认模板参数放一秒钟,然后为最后一个参数命名。

您的主变量模板如下所示:

template <typename Tag, typename... Tags, typename _Unnamed>
const std::string config_data = ...;

您的第一个专业是:

template <>
const std::string config_data<WantName> = ...;

因此,在这个专业领域中,Tag=WantNameTags={}_Unnamed是……究竟是什么?未指定。确实,没有办法真正指定它。可以使用尾随默认模板参数,这总是可以的。但是,一旦您尝试将其专门化,就不可能做到。

在C ++ 20中,您将可以适当地对此进行约束:

template <DerivedFrom<Want> Tag, DerivedFrom<Want>... Tags>
const std::string config_data = ...;

到那时,您真的需要SFINAE吗?不完全确定您将从中得到什么。如果您只是放下它,一切都会很好。