c++ 为什么编译器会抱怨歧义

tct7dpnv  于 2023-10-20  发布在  其他
关注(0)|答案(3)|浏览(142)

在下面的代码中,编译器会抱怨二义性。为什么?很明显我传递的是non const non & object。

#include <stdio.h>
#include <ranges>
#include <vector>
#include <string>
#include <iostream>

using namespace std;
template<class T>
string toStr(const T& obj)
{
    return obj.toStr();
}

template<class T>
string toStr(T val)
{
    return std::to_string(val);
}
struct A
{
  string toStr(){return "A";}  
};
int main()
{
    A a;

    cout << toStr(static_cast<const A&>(a)) << '\n';

    return 0;
}

我试着在不同的编译器上编译它。效果是一样的。我甚至试着把它转换成想要的const & type。不知道

cout << toStr(static_cast<const A&>(a)) << '\n';

编辑:

> main.cpp: In function ‘int main()’: main.cpp:35:18: error: call of
> overloaded ‘toStr(const A&)’ is ambiguous    35 |     cout <<
> toStr(static_cast<const A&>(a)) << '\n';
>       |             ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp:17:8: note: candidate: ‘std::string toStr(const T&) [with T = A; std::string
> = std::__cxx11::basic_string]’    17 | string toStr(const T& obj)
>       |        ^~~~~ main.cpp:23:8: note: candidate: ‘std::string toStr(T) [with T = A; std::string = std::__cxx11::basic_string]’    23
> | string toStr(T val)
>       |        ^~~~~

真正令人沮丧的是来自error msg的这一行:错误:调用
重载的'toStr(const A&)'是不明确的如果只有一个函数具有这样的签名,那么这^怎么可能是不明确的呢?

iqjalb3h

iqjalb3h1#

cout << toStr(a) << '\n';

A类型的左值传递给toStr()
它现在可以将其转换为const A&以匹配

const T& obj

或将其复制到A以进行匹配

T val

没有一个比另一个更专业,因此是模糊的。
请注意,A::toStr()应该是const,才能在string toStr(const T& obj)版本中使用。
然后你可以添加一些约束来选择正确的重载,比如检查类型是否有toStr成员函数:

template <class, class = void>
struct has_toStr : std::false_type {};

template <class T>
struct has_toStr<T, std::void_t<decltype(std::declval<T&>().toStr())> >
    : std::true_type {};

template <class T>
inline constexpr bool has_toStr_v = has_toStr<T>::value;

template <class T>
std::string toStr(const T& obj)
    requires has_toStr_v<T>
{
    return obj.toStr();
}

template <class T>
std::string toStr(T val)
    requires(not has_toStr_v<T>)
{
    return std::to_string(val);
}
7xzttuei

7xzttuei2#

按引用和按值参数不能用于消除重载的歧义。(最多可以使用不同类型的引用,例如:const T &T &&,但在这里无关紧要。)
这里的问题是函数不是SFINA友好的(即,函数体对于特定T无效不足以在重载解析期间拒绝该重载)。
你需要明确地阐明这些条件,例如:使用requires

template <typename T> 
std::string toStr(const T &obj)
requires requires{{obj.toStr()} -> std::convertible_to<std::string>;}
{
    return obj.toStr();
}

template <typename T>
requires std::is_arithmetic_v<T>
std::string toStr(T val)
{
    return std::to_string(val);
}

我还做了一些表面上的改变。您还必须const-限定AtoStr()
在C++20之前,它看起来像这样:

template <
    typename T,
    std::enable_if_t<
        std::is_convertible_v<decltype(std::declval<const T &>().toStr()), std::string>,
        std::nullptr_t
    > = nullptr
> 
std::string toStr(const T &obj)
{
    return obj.toStr();
}

template <
    typename T,
    std::enable_if_t<std::is_arithmetic_v<T>, std::nullptr_t> = nullptr
> 
std::string toStr(T val)
{
    return std::to_string(val);
}
mnemlml8

mnemlml83#

阅读完整的信息,而不仅仅是第一行:toStr(const A&): candidatetoStr(T val): candidate有歧义。
假设toStr(a),你会如何选择?通过引用还是通过值传递?模棱两可。
试图通过使用static_cast<const A&>来强制类型不会改变任何东西,通过引用或通过值传递?
一般来说,不要重载带有pass-by-ref和匡威-value的函数,否则会导致歧义。

相关问题