禁用默认模板,仅通过sfinae使用专门化

时间:2014-10-06 15:59:32

标签: c++ templates sfinae overload-resolution

考虑以下系统:

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之间的便携式解决方案是必须的,但我使用的是最新版本。

3 个答案:

答案 0 :(得分:1)

如果你这样做

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

按预期工作:Live example

答案 1 :(得分:1)

虽然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

当然,额外的好处是编译器会在调用/实例化时使用正确的错误消息识别错误,而不是一些随机的static_assert / linker错误。成功了!

答案 2 :(得分:1)

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

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