3

我在回答这个问题后想出了这个

我有一个简单的函数模板(C++11):

template<class elem_t, class list_t>
bool in_list(const elem_t& elem, const list_t& list) {
   for (const auto& i : list) {
      if (elem == i) {
         return true;
      }
   }
   return false;
}

但是 GCC 发出警告,因为它似乎不喜欢将模板参数推断为 std::initializer_list。所以,不假思索,我做了一个专业化:

template<class elem_t>
bool in_list(const elem_t& elem, std::initializer_list<elem_t> list) {
   for (const auto& i : list) {
      if (elem == i) {
         return true;
      }
   }
   return false;
}

这行得通。没有更多的警告。但是当我再次查看并考虑时,我记得 C++ 不支持函数模板上的部分模板特化。但这似乎就是这样。我唯一的猜测是这是允许的,因为 std::initializer_list 仍然依赖于模板参数,所以它本质上是一个不同的模板。但是我不确定这是否应该是这样(没有关于模板不重载的问题吗?)。

接受这一点是标准行为吗?为什么?

作为一个额外的问题,为什么 GCC 不喜欢将模板参数推断为 std::initializer_list?期望我复制和粘贴代码并用 std::initializer_list 替换参数似乎很愚蠢。

警告信息:

test.cpp: In function ‘int main()’:
test.cpp:33:43: warning: deducing ‘const list_t’ as ‘const std::initializer_list<int>’ [enabled by default]
test.cpp:6:6: warning:   in call to ‘bool in_list(const elem_t&, const list_t&) [with elem_t = int, list_t = std::initializer_list<int>]’ [enabled by default]
test.cpp:33:43: warning:   (you can disable this with -fno-deduce-init-list) [enabled by default]

当被调用时in_list(3, {1, 2, 3, 4, 5});

编辑:显然,将模板参数推断为 initializer_list 是根据我的 GCC 版本(引用)的工作草案的扩展。所以新的问题:这仍然是最终 c++11 标准的扩展吗?如果是这样,这意味着我有必要为符合标准的代码添加第二个函数。感谢你的帮助!

EDIT2: GCC 4.7 的编译器方言标志似乎已被删除,因此问题似乎已解决,但我不知道它是如何解决的。

4

2 回答 2

4

使用@Ben Voigt 在另一个答案的评论中所说的话,我收集了一些相关的标准引用:

§14.5.6.2

一个函数模板可以被其他函数模板和普通(非模板)函数重载。普通函数与函数模板无关(即,它从不被视为特化),即使它与可能生成的函数模板特化具有相同的名称和类型

所以这排除了你正在做的函数模板特化,因为即使两个函数模板重载可能会生成相同的函数,它也不是特化。所以超载了。

这样的专业化是不同的功能,并且不违反单一定义规则(3.2)。

所以它们是不同的功能,这就是它不会出错的原因。

§14.5.6.2.1

如果函数模板被重载,则函数模板特化*的使用可能会产生歧义,因为模板参数推导 (14.8.2) 可能会将函数模板特化与多个函数模板声明相关联。

这是针对我们都已经看到的内容的启动,即in_list(a, b)where bis aninitializer_list似乎与两个函数模板匹配。

(*注意,这里的“函数模板特化”并不意味着特化一个函数模板,它意味着一个已经用一个类型实例化的函数模板。所以 with template<typename T> f();,f<int>()是一个函数模板特化。)

所以我们使用所谓的重载函数模板的部分排序来解决这个问题:

在以下上下文中使用重载函数模板声明的部分排序来选择函数模板特化所引用的函数模板:

— 在调用函数模板特化 (13.3.3) 的重载决议期间;

— 当一个函数模板特化的地址被取用时;

— when a placement operator delete that is a function template specialization is selected to match a placement operator new (3.7.4.2, 5.3.4);

— when a friend function declaration (14.5.4), an explicit instantiation (14.7.2) or an explicit specialization (14.7.3) refers to a function template specialization.

Ok, so that's when partial ordering is for. This is what it does:

Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.

And then you get into the long and laborious process of determining which template is more specialised, which you can read about if you want, but it's really complicated and I probably don't understand it all (and plus, I don't have enough time to write about it :)).

于 2012-04-29T18:05:30.127 回答
3

这不是部分专业化。您正在做的是重载功能。

于 2012-04-29T17:27:16.763 回答