【问题标题】:Disable default template and only use specialization through sfinae禁用默认模板,仅通过 sfinae 使用专业化
【发布时间】:2014-10-06 15:59:31
【问题描述】:

考虑以下系统:

template<typename T>
    struct wrapper
    {
        operator T * () { return nullptr; }
    };

template<typename Ret, typename T>
    Ret func(T);

template<>
    int func(float * in)
    {
        std::cout << "long";
    }

template<>
    long func(float * in)
    {
        std::cout << "int";
    }

包装器的目的是允许它衰减到模板化的类型(它是该类型缓冲区的包装器)。此外,我有一组函数是模板的模板特化。这是为了规避仅基于返回类型重载时的常见错误。

但这不起作用,如下所述:

// the following should work, but doesn't because it's instantiating 
// the func<ret, wrapper<float>> which doesn't exist resulting in a linker error
// instead of selecting the int func(float *) overload
wrapper<float> w;
func<int>(w);

相反,我希望这会产生编译时错误(但同样会产生链接时错误):

// the following should generate a compile-time error
// since no explicit overload for int func(int *) exists
wrapper<int> w2;
func<int>(w2);

所以理想情况下,我想禁用原始模板(如果可能的话,也许通过 sfinae?)这样重载解决方案只考虑显式特化,如果找不到匹配项,则会生成编译时错误。这个可以吗?

clang 和 msvc 之间的便携式解决方案是必须的,但我使用的是两者的最新版本。

【问题讨论】:

  • 您的问题不在于重载解析,而在于模板参数推导不考虑隐式转换。
  • @P0W 因为你不能超载这两个。唯一的区别是返回类型。
  • @TC 啊,我明白了,谢谢

标签: c++ templates sfinae overload-resolution


【解决方案1】:

如果你这样做

template<typename Ret> Ret func(float*);

它按预期工作:Live example

【讨论】:

  • 感谢您的想法 - 这是解决方案的一部分(请参阅我的回答)
【解决方案2】:

虽然 Jarod 的回答解决了其中一个问题,但我仍然需要一种方法来重载函数参数(在这种情况下会产生“不匹配模板”错误)——我可能没有在 OP 中说明这一点。

我突然意识到,参数类型总是依赖于返回类型。然后我可以构建一个辅助结构,它会做 sfinae:

template<typename T>
    struct option_of;

template<>
    struct option_of<int>
    {
        typedef float value;
    };

template<>
    struct option_of<long>
    {
        typedef double value;
    };

然后默认模板将如下所示:

template<typename Ret>
    Ret func(typename const option_of<Ret>::value *);

然后可以像这样构造重载:

template<>
    int func(const float * in)
    {
        std::cout << "long";
    }

template<>
    long func(const double * in)
    {
        std::cout << "int";
    }

-没有问题。请注意,返回和参数类型的任何其他组合都是无效的(因为它们不是原始模板的特化,它只考虑我给它的选项)。这也将唯一的重载决议减少到两个重载,从而使这成为可能:

wrapper<float> w;
func<int>(w); // works
func<long>(w); // invalid, because no combination of long and float exists according to option_of

wrapper<int> w2; // works, but
func<int>(w2); // invalid because option_of doesn't consider int's

当然,额外的好处是编译器在调用/实例化时使用正确的错误消息识别错误,而不是一些随机的静态断言/链接器错误。成功!

【讨论】:

  • 在您的示例中,不仅参数取决于返回类型,而且还取决于其他方式。如果你创建 return_of::value 那么你的模板参数可以是隐式的,你可以在没有模板参数的情况下调用 func(w2)。
【解决方案3】:

另一种方法可能是使用 static_assert:

template<typename Ret, typename T>
Ret func(T) {
  static_assert(false, "template specialization required");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-10-03
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 2017-12-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多