C++ 中的模板匹配函数重载问题 20

Trouble with Template Matching Function Overloads in C++20

提问人:carraro 提问时间:11/6/2023 更新时间:11/6/2023 访问量:89

问:

我目前正在处理一个 C++20 项目,我需要使用模板匹配实现一系列重载函数。我正在尝试创建一组函数模板,这些模板可以区分不同类别的类型(例如,整型类型、浮点型和自定义类)并使用专门的算法处理每种类型。

但是,我遇到了一个问题,即编译器没有像我预期的那样选择最专业的模板重载。我简化了我的代码来说明这个问题:

#include <iostream>
#include <type_traits>

// Overload for integral types
template<typename T>
requires std::is_integral_v<T>
void process(T value) {
    std::cout << "Integral: " << value << std::endl;
}

// Overload for floating-point types
template<typename T>
requires std::is_floating_point_v<T>
void process(T value) {
    std::cout << "Floating-point: " << value << std::endl;
}

// Overload for custom class
class MyCustomClass {};

template<>
void process<MyCustomClass>(MyCustomClass value) {
    std::cout << "MyCustomClass instance" << std::endl;
}

int main() {
    process(10);        // Should call the integral overload
    process(3.14);      // Should call the floating-point overload
    process(MyCustomClass()); // Should call the custom class overload
}

当我使用 编译此代码时,我收到一个错误,表明编译器无法推断出正确的重载:g++ (GCC) 10.2.0

error: template-id ‘process’ for ‘void process(MyCustomClass)’ does not match any template declaration’

我正在寻找以下几点的指导:

  1. 为什么编译器没有为 选择正确的重载?MyCustomClass
  2. 有没有更好的方法来为 C++20 中的这种基于类型的重载构建我的函数模板?
  3. 这个问题可能与我正在使用的概念有关,还是与我的函数模板专用化有关?

我已经检查了 CPPReference 和其他 Stack Overflow 问题,但没有找到解决我问题的解决方案。我正在用标志编译。-std=c++20

C++ 模板 C++20

评论

4赞 NathanOliver 11/6/2023
不要专业化,超载。template<> void process<MyCustomClass>(MyCustomClass value) -> void process(MyCustomClass value)
1赞 Rerito 11/6/2023
这是一个函数模板,而不是类模板。当您有显式签名时,只需在没有任何模板的情况下进行普通重载。
1赞 user12002570 11/6/2023
实际问题是,你的代码中没有主模板来显式专用化!因此,您可以添加主模板,也可以将显式专用化更改为非模板重载。

答:

1赞 Rerito 11/6/2023 #1

如注释中所述,当您对函数模板具有显式的“专用化”时,只需编写一个普通的非模板重载:

void process(const MyCustomClass& object)
{
    // ...
}

但是,如果您有模板过载,请注意可能发生的完美匹配:

template< typename T >
void process(T&& obj) // generic implementation using perfect-forwarding
{
    // ...
    std::cout << "Generic template" << std::endl;
}

使用上述两个重载,以下调用:

process(MyCustomClass{});

MyCustomClass obj{};
process(obj);

将使用通用实现并打印,因为模板是完全匹配的(对于< - 注意第一种情况下的右值 ref 和第二种情况下的 for)。Generic templateMyCustomClass&&MyCustomClass&

为避免这种情况,您必须禁用专用类型的 cv-ref 组合模板,例如:

template< typename T >
    requires(!std::is_same_v< MyCustomClass, std::remove_cvref_t< T > >)
void process(T&& obj)
{
    // ...
}
4赞 user12002570 11/6/2023 #2

问题在于,您的代码中没有显式专用的主模板

这意味着,若要解决此错误,可以提供主模板,也可以将显式专用化更改为非模板重载。

方法 1

这里我们提供了一个主模板,如下所示:

//added this primary template
template<typename T>
void process(T);

//other code as before

工作演示


方法 2

第二种方法是只提供一个普通的非模板重载,而不是一个显式的专用化:

//note the template<> has been removed from here
void process(MyCustomClass value) {
    std::cout << "MyCustomClass instance" << std::endl;
}

工作演示