模板专门化或函数重载

问题描述 投票:0回答:1

我创建了两个类 - 一个可以通过转换运算符转换为另一个:

struct MyClass{};

struct MyClass2{
    operator MyClass() const { return MyClass{}; }
};

和专门的模板函数(

std::initializer_list<MyClass>
的专门化):

template<typename T>
void foo(std::initializer_list<T>)
{
}

template<>
void foo(std::initializer_list<MyClass>)
{
    std::cout << "specialised template foo<>()";
}

当我尝试使用初始化列表混合

foo
MyClass
来调用
MyClass2
时:

foo({MyClass{}, MyClass2{}, MyClass2{}});

编译器反对(因为我混合了两种不同的类型):

<source>:35:8: error: no matching function for call to 'foo(<brace-enclosed initializer list>)'
   35 |     foo({MyClass{}, MyClass2{}, MyClass2{}});
      |     ~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:19:6: note: candidate: 'template<class T> void foo(std::initializer_list<_Tp>)'
   19 | void foo(std::initializer_list<T>)
      |      ^~~
<source>:19:6: note:   template argument deduction/substitution failed:
<source>:35:8: note:   deduced conflicting types for parameter '_Tp' ('MyClass' and 'MyClass2')
   35 |     foo({MyClass{}, MyClass2{}, MyClass2{}});
      |     ~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

但是出于某种原因,如果我为与模板专业化中相同的类型添加非模板重载

foo
- 它就像一个魅力!

void foo(std::initializer_list<MyClass>)
{
    std::cout << “overloaded foo()";
}

int main(int argc, char **argv) {
    foo({MyClass{}, MyClass2{}, MyClass2{}}); // Program stdout : overloaded foo()
}

我猜当编译器查找函数时,非模板重载优先于模板。但为什么它适用于重载而不适用于模板专业化呢?这是未定义的行为吗?或者这样做是完全合法的吗?

实例:https://godbolt.org/z/5ohhK1jeb

c++ c++11 templates initializer-list
1个回答
0
投票

为什么它适用于重载而不适用于模板专业化?

  • 专业化不参与重载选择,只有主模板参与。 (选择主模板后,“使用”专业化进行定义)。

  • 模板推导“忽略”类型转换。

  • {..}
    没有类型,只能在很少的上下文中推断出来。由于其类型并不完全相同,所以
    {..}
    不能推导出为
    std::initializer_list<T>

使用重载

void foo(std::initializer_list<MyClass>)
,不会发生推论,并且可能会检查我们是否可以将参数与该函数匹配,并且提供的
{MyClass{}, MyClass2{}, MyClass2{}}
确实可以是
std::initializer_list<MyClass>

这就是为什么在一般情况下应该优先选择重载而不是专门化。

这是未定义的行为吗?或者这样做是完全合法的吗?

不存在未定义的行为。 超载是完全合法的。

© www.soinside.com 2019 - 2024. All rights reserved.