【问题标题】:C++: Why is name dependent in constructor, not in static member functionC ++:为什么名称依赖于构造函数,而不是静态成员函数
【发布时间】:2020-01-24 20:44:43
【问题描述】:

我对@9​​87654321@ 描述的场景有一些后续问题,我发现this post 特别有助于推理我的问题。

背景

在我最初的问题中,我有兴趣重载 std::make_shared 函数以处理部分指定的模板参数:

std::make_shared<Container<1>::Internal>(arguments...);

InternalContainer 是结构体,它们都有一个模板参数,Internal 的一个是派生的。 (完整的代码在下面可用)。当直接指定Container 的模板参数时(上一行中的1),可接受的解决方案完美运行。

问题/问题

然后我尝试从不同的constexpr 上下文中指定模板参数:

std::make_shared<Container<A>::Internal>(arguments...);

其中A 是上述行所在的第三个结构的模板参数。 在这里我意识到,

  1. ...在构造函数中,行不通,必须改成std::make_shared&lt;Container&lt;A&gt;::template Internal&gt;(arguments...);
  2. ...在成员函数中,该行工作正常。

问题

为什么会这样?

代码

代码可以试用here

#include <memory>
#include <type_traits>

/// Container that is used in order to partially specify template arguments
template <int A> struct Container {
  /// Contained type, of which the template arguments are deduced.
  template <int B> struct Internal {
    explicit Internal(std::integral_constant<int, B> fu) { (void)fu; }
  };
};

/// Own make shared function / overload
namespace std {
template <template <int> typename partial, typename... Args> auto make_shared(Args &&... args) {
  using target_type = decltype(partial{std::declval<Args>()...});
  return std::make_shared<target_type>(std::forward<Args>(args)...);
}
} // namespace std

/// Some struct which uses its own template argument in order to create
template <int A> struct Producer {
  template <typename... T> explicit inline Producer(T &&... t) noexcept {
    auto const works = std::make_shared<Container<1>::Internal>(std::forward<T>(t)...);
    // XXX: the following fails if "template" is omitted
    auto const fails = std::make_shared<template Container<A>::Internal>(std::forward<T>(t)...);
  }

  template <typename... T> static auto test(T &&... t) noexcept {
    /// XXX: Here, it works without specifying "template"
    return std::make_shared<Container<A>::Internal>(std::forward<T>(t)...);
  }
};

int main() { Producer<1>{std::integral_constant<int, 8>{}}; }

编译

  • 使用的编译器:g++-9.1g++-10.0
  • 编译命令:g++ -std=c++2a sol.cc

错误

当我在构造函数中省略“模板”时,会生成以下错误消息。

sol.cc: In instantiation of ‘Producer<A>::Producer(T&& ...) [with T = {std::integral_constant<int, 8>}; int A = 1]’:
sol.cc:34:58:   required from here
sol.cc:25:29: error: dependent-name ‘Container<A>::Internal’ is parsed as a non-type, but instantiation yields a type
   25 |     auto const fails = std::make_shared<Container<A>::Internal>(std::forward<T>(t)...);
      |                        ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sol.cc:25:29: note: say ‘typename Container<A>::Internal’ if a type is meant

自己对这个问题的看法

name lookup 期间,编译器确定名称是否表示类型/模板。

如果在知道实际模板参数之前无法查找名称,则它是dependent-name。 据我了解,这里就是这种情况(至少在构造函数中)。因此,在构造函数中,编译器无法确定 Internal 表示模板,而在静态成员函数中却是。

这可能是Container&lt;A&gt;::Internal 在构造函数中成为依赖名称的原因,以防编译器自动推断结构的类型。 但在我的用例中,我明确说明了它(main() 中的Producer)。

注意

我试图提供一个simpler example,但在那个例子中这个错误没有发生,我想什么 我上面提供的比较少。

【问题讨论】:

  • 尝试添加调用静态函数的语句。您的代码显式构造了一个对象,因此将调用构造函数,这会强制编译器诊断构造函数的代码,但不会在任何地方调用静态函数。模板的处理相当复杂,并且一些编译器非常积极地推迟模板实例化 - 这可能会影响编译器何时或是否有足够的信息来发出诊断

标签: c++ templates name-lookup


【解决方案1】:

在您提供的代码中,test 函数不会在任何地方调用,因此它永远不会被实例化。如果要对其进行实例化,那么由于缺少template,编译器需要将其诊断为格式错误。

【讨论】:

    猜你喜欢
    • 2015-02-27
    • 2013-01-16
    • 2011-08-13
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多