【问题标题】:Removing an unused overload causes compile error?删除未使用的重载会导致编译错误?
【发布时间】:2016-01-21 18:41:17
【问题描述】:

我正在考虑删除一些未使用的重载,并触发了编译错误,编译器称这是模板替换错误。但是我认为“替换失败不是错误”,无论如何,为什么删除过载会导致它?

简单的开始:

#include <string>

int ParseInt(const char *);
int ParseInt(std::string);

bool F(int(*)(const char *));

bool User() {
  return F(ParseInt);
}

这里,User() 使用解析例程的地址调用 F。一切都很好。 ParseInt 已重载,但只有一个重载与 F 的签名匹配。

输入 F 的模板化重载:

bool F(int(*)(const char *));

template <typename T>
struct MetaDeduce {
  typedef typename T::X type;
};

template <typename T>
typename MetaDeduce<T>::type F(const T&);

现在有一个奇怪的 F 模板重载,但没关系,因为函数指针无论如何都没有名为 X 的成员。一切都编译好了,一切都很好。

直到……

#include <string>

int ParseInt(const char *);
// int ParseInt(std::string);  // commenting this out caused a compiler error!

bool F(int(*)(const char *));

template <typename T>
struct MetaDeduce {
  typedef typename T::X type;
};

template <typename T>
typename MetaDeduce<T>::type F(const T&);

bool User() {
  return F(ParseInt);
}

从 Godbolt (http://goo.gl/2Yd04p) 可以看出,这会产生一个奇怪的编译错误:

10 : error: type 'int (const char *)' cannot be used prior to '::'
     because it has no members
typedef typename T::X type;
^
14 : note: in instantiation of template class 'MetaDeduce<int (const char *)>'
     requested here
typename MetaDeduce<T>::type F(const T&);
^

WTF???看起来编译器正在抱怨替换失败,但为什么以前不是问题?无论如何,我认为替换失败不是错误!怎么回事?

【问题讨论】:

    标签: c++ templates sfinae overloading template-argument-deduction


    【解决方案1】:

    这是由两种语言属性之间的微妙交互引起的

    1. SFINAE 仅适用于即时上下文。您的MetaDeduce&lt;T&gt; 没有在这样一个直接的上下文中定义,这使得typename MetaDeduce&lt;T&gt;::type 成为一个硬错误。但 SFINAE 确实适用于 typename T::X,即使 ParseInt(std::string) 重载被注释掉,使用它也会编译您的代码。

      template <typename T>
      typename T::X F(const T&);
      

    Live Example(请注意,您会收到链接器错误,因为您尚未定义函数)

    1. 那么,如果typename MetaDeduct&lt;T&gt;::type 是罪魁祸首,为什么它可以与ParseInt 的两个重载一起工作?好吧,考虑一下如果你只有模板FParseInts 会发生什么。为了方便起见,给该模板F 一个 bool 返回类型,以便在替换失败时忽略。

      int ParseInt(const char *);
      int ParseInt(std::string);
      
      template <typename T>
      bool F(T const&);
      
      bool User() { return F(ParseInt); } // error, cannot deduce template argument
      

    Live Example

    由于ParseInt 的两个重载并且在调用F(ParseInt) 中没有额外信息,编译器无法推断出ParseInt 的哪个版本应该与模板参数T 匹配。在这个 sn-p 的上下文中,这将导致一个硬错误(因为您将有一个空的重载集),但是对于额外的非模板重载 F(int(*)(const char*)) 它不会(因为 ParseInt(const char*) 将匹配)。请注意,由于此处的参数推导失败,如果返回类型为 typename MetaDeduce&lt;T&gt;::Type,则此处甚至不会发生参数替换。

    实际上,ParseInt 的两个重载使您免受非立即上下文中的替换失败的影响。一旦你拿走其中一个,参数推导成功,非立即替换失败导致硬错误。

    这有点像闯红灯过马路,因为两辆迎面而来的卡车在撞到你之前就相互碰撞而得救。只有一辆卡车,你会受到打击。

    【讨论】:

    • typename MetaDeduct&lt;T&gt;::type 在返回类型中是在直接上下文中 - 如果 MetaDeduct 没有名为 type 的成员类型,你会得到 SFINAE。但是,MetaDeduct 的定义不是直接上下文,并且错误发生在那里,所以这是一个硬错误。
    猜你喜欢
    • 1970-01-01
    • 2019-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 2017-06-11
    • 2019-08-31
    • 2011-01-26
    相关资源
    最近更新 更多