【问题标题】:Overloaded templated function declaration order重载的模板函数声明顺序
【发布时间】:2014-06-24 02:49:59
【问题描述】:

我有一些代码读起来像这样 SSCCE:

#include <map>
#include <string>
#include <functional>

using std::map;
using std::string;
using std::function;
using std::forward;

template<typename ToType, typename... FromTypes>
ToType construct(FromTypes&&... fromTypes) {
  return ToType(forward<FromTypes>(fromTypes)...);
}

class Variant {
public:
  map<string, Variant> toMap() const;
};

map<string, Variant> Variant::toMap() const {
  return {
      {"test", {}},
      {"test1", {}},
    };
}

// #1
template <typename MapType>
MapType variantToMap(Variant const& v) {
  // I expect an error on the following line because it's referencing the function labeled #4 which has not been declared yet.
  return variantToMap<MapType>(v, construct<typename MapType::key_type, string>, construct<typename MapType::mapped_type, Variant>);
}

// #2
template <typename MapType>
MapType variantToMap(Variant const& v, function<typename MapType::key_type(string)> const& keyConvert) {
  // I expect an error on the following line because it's referencing the function labeled #4 which has not been declared yet.
  return variantToMap<MapType>(v, keyConvert, construct<typename MapType::mapped_type, Variant>);
}

// #3
template <typename MapType>
MapType variantToMap(Variant const& v, function<typename MapType::mapped_type(Variant)> const& valueConvert) {
  // I expect an error on the following line because it's referencing the function labeled #4 which has not been declared yet.
  return variantToMap<MapType>(v, construct<typename MapType::key_type, string>, valueConvert);
}

// #4
template <typename MapType>
MapType variantToMap(Variant const& v, function<typename MapType::key_type(string)> const& keyConvert, function<typename MapType::mapped_type(Variant)> const& valueConvert) {
  MapType res;
  for (auto pair : v.toMap()) {
    res[keyConvert(pair.first)] = valueConvert(pair.second);
  }

  return res;
}

int main() {
  Variant a;

  // #1
  variantToMap<map<string, Variant>>(a);

  // #2
  {
    int counter = 0;
    variantToMap<map<int, Variant>>(a, [&counter](string) -> int { return ++counter; });
  }

  // #3
  variantToMap<map<string, int>>(a, [](Variant) -> int { return 42; });

  // #4
  {
    int counter = 0;
    variantToMap<map<int, int>>(a, [&counter](string) -> int { return ++counter; }, [](Variant) -> int { return 42; });
  }

  return 0;
}

编译和工作正常。

但是,由于它被声明为无序,我预计这会引发错误。我认为它不是的原因与这些函数都是模板化的事实有关。但是,这对我来说仍然有点混乱,因为(例如)在 variantToMap #1 中对 variantToMap #4 的调用有 3 个参数,这意味着

Clang 和 GCC 是否不恰当地接受这些函数,或者这是允许的?如果允许,其理由是什么?

需要注意的是,我已经对这些函数进行了重新排序,我只是好奇为什么它首先会起作用。

【问题讨论】:

  • 请务必说明您预计错误的确切位置以及原因。将 cmets 放在相应的行上,例如// I expect an error here because this line uses an indefined identifier foobar.
  • @n.m.我已添加要求的信息。

标签: c++ templates language-lawyer


【解决方案1】:

C++ 使用two phase name lookup 来解析模板定义中的标识符。 (除非你有 MSVC,它只使用第二阶段)。

当模板被声明/定义时,所有不依赖于模板参数的名称都会被解析。当它被实例化时,确实依赖于模板参数的名称被解析。这一次,在实例化点可见的名称参与其中,并且可以在查找过程中找到。

在您的情况下,内部 variantToMap 调用取决于模板参数,因此在调用外部函数之前它们不会被解析。此时所有 4 个变体都可见并且可以找到。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    相关资源
    最近更新 更多