【问题标题】:Why is this function overload with argument type conversion ambiguous?为什么这个函数重载参数类型转换不明确?
【发布时间】:2014-11-14 15:09:34
【问题描述】:

有人能解释一下这个函数重载决议是如何模棱两可的吗?

给定:

/// This type *contains* a @c T.
template< typename T >
class User_Type
{
public:
    /// This <em>conversion constructor</em> is a key part of it's API;
    ///   it won't likely change.
    User_Type( T const & ar_data )
      : m_data( ar_data )
    {}
private:
    T m_data;
};

/// @c some_value is just a templated function that generates a @c T.
template< typename T > T some_value();
template<> char some_value();
template<> int some_value();

/// This overloaded, non-templated function represents some third-party
///   code somewhere else; it's API can't be changed.
void other_function( User_Type< char > const& );
void other_function( User_Type< int > const& );

/// This is user-code.  It's contents exercise some aspect of the 'User_Type' API.
///   This code can change.
template< typename T >
void function()
{
    other_function( some_value< T >() ); /* AMBIGUOUS CALL */

    User_Type< T > user_var = some_value< T >(); /* UNAMBIGUOUS CONVERSION */
    other_function( user_var ); /* UNAMBIGUOUS CALL */
}
template void function< char >();
template void function< int >();

并使用g++-4.9 -Wall -Wextra 进行编译,我收到以下错误:

In instantiation of ‘void function() [with T = char]’:
    error: call of overloaded ‘other_function(char)’ is ambiguous
    note: candidates are:
    note: void other_function(const User_Type<char>&)
    note: void other_function(const User_Type<int>&)
In instantiation of ‘void function() [with T = int]’:
    error: call of overloaded ‘other_function(int)’ is ambiguous
    note: candidates are:
    note: void other_function(const User_Type<char>&)
    note: void other_function(const User_Type<int>&)

我希望other_function( char ) 的最佳匹配是other_function( User_Type&lt; char &gt; const&amp; )other_function( int ) 的最佳匹配是other_function( User_Type&lt; int &gt; const&amp; )

我了解other_function 的每个参数都必须进行类型转换。我希望charUser_Type&lt; char &gt; 是比charUser_Type&lt; int &gt; 更好的选择,charint 促销可以允许这种选择。我希望intUser_Type&lt; int &gt; 是比intUser_type&lt; char &gt; 更好的选择,intchar 转换可以允许这种选择。

此外,如果我从T 创建一个本地User_Type&lt; T &gt; user_var,那么我可以明确地调用other_function( user_var )。从语义上讲,这应该等同于第一个原始语句。

【问题讨论】:

  • 我觉得我以前遇到过这种情况,但我正在画一个空白。我相信它与评估模板的顺序有关,即 some_value 的返回类型在处理函数之后才推导出来,因此它是一个模棱两可的调用。与您无法将公共typedef T some_type; 添加到User_Type&lt;T&gt; 并让编译器找出User_Type&lt;T&gt;::some_type 是一种没有完全限定它的类型的原因相同,即typename User_Type&lt;T&gt;::some_type

标签: c++ implicit-conversion overloading


【解决方案1】:

由于other_function(char)other_function(int) 没有完全匹配,

other_function( some_value< T >() );

必须隐式转换其参数以匹配以下之一:

void other_function( User_Type< char > const& );
void other_function( User_Type< int > const& );

User_Type&lt;char&gt; 有 ctor User_Type&lt;char&gt;( char const&amp; ),它接受 int,而 User_Type&lt;int&gt; 有 ctor User_Type&lt;int&gt;( int const&amp; ),它接受 char

您的转化属于“用户定义的转化序列”的排名,并且由于这两种转化都是可能的,因此它们都包含在具有相同排名的重载集中。因此,调用是模棱两可的。 (有关详细信息,请参阅标准中的 13.3.3.2 对隐式转换序列进行排序。)

【讨论】:

  • User_Type&lt; char &gt;( char const&amp; ) 的转换序列不会比User_Type&lt; char &gt;( int const&amp; ) 有更好的排名吗?第二个需要转换参数,(精度损失)。第一个是直接匹配。
  • 我之前尝试过阅读§13.3.3.x;我从来没有完全摸过它。
  • 不完全。标准中的措辞说:“如果用户定义的转换序列 U1 包含 相同的用户定义的转换函数或构造函数或聚合初始化,那么它们是比另一个用户定义的转换序列 U2 更好的转换序列,并且U1 的第二个标准转换序列更好......”在这种情况下,您有两个具有 不同 构造函数的 UD 转换。如果User_Type 本身是具有User_Type(int)User_Type(char) ctors 的类型,那么就会产生歧义......
  • 很遗憾,User_Type 必须模板化;我不会尝试进行类型擦除。对于上下文,User_Type包含T 的实例。
  • 我已经用 cmets 更新了问题,描述了哪些部分是“固定的”,哪些部分可以更改。
【解决方案2】:

我认为这里的问题是试图根据合格的转换来选择重载。我可以取出一些模板并得到相同的结果:

class User_Type_char
{
public:
    User_Type_char(char const &) {}
};

class User_Type_int
{
public:
    User_Type_int(int const &) {}
};

void other_function( User_Type_char const& );
void other_function( User_Type_int const& );

template< typename T >
void function()
{
    other_function( 'a' ); /* AMBIGUOUS CALL */
}

问题是重载列表中没有完美匹配,所以我们开始检查转换。但反过来,charint 有可用的隐式转换,所以不清楚你想要哪一个。

如果对于相同的代码,我更改了第一个类的构造函数

class User_Type_char
{
public:
    User_Type_char(const char*) {}
};

现在同一个调用变得明确并调用 User_Type_int 版本。

【讨论】:

  • 嗯,'User_Type'的接口是固定的;我试图确定这个 API 的所有“预期”用途是什么。我希望通过函数参数从TUser_Type&lt;T&gt; 的隐式转换得到支持;这种隐式转换发生在函数的返回类型上。 :-/
  • 我用 cmets 更新了问题,描述了哪些部分是“固定的”,哪些部分可以更改。
  • 我回答的重点不是建议一个新界面,而是表明问题与类型转换有关,而不是与模板有关(hs_ 对此进行了更好的解释)。 AFAICT,您不能像这样删除两个步骤来进行重载解析。您需要将类型转换和重载解析的步骤分开。您可以在两者之间添加一个函数模板,添加 User_Type 像 template&lt;class T&gt; void call_other_function(T const &amp; val) { other_function(User_Type&lt;T&gt;(val)); }
  • 啊,用模板函数包装重载函数。嗯,如果用户真的需要频繁调用重载函数,这可能会奏效。
  • 其实我只是想加template&lt; typename T &gt; User_Type&lt; T &gt; make_user_type( T const &amp; );。那么模棱两可的电话other_function( some_value&lt; T &gt;() ) )可以变成other_function( make_user_type( some_value&lt; T &gt;() ) )。它有点冗长,但比将重载函数包装在一组模板函数中要好。
猜你喜欢
  • 2016-05-18
  • 2020-06-14
  • 2013-11-18
  • 1970-01-01
  • 2014-11-18
  • 1970-01-01
  • 2010-10-17
  • 2017-05-11
  • 2016-07-28
相关资源
最近更新 更多