在签名中使用另一个成员模板函数的行外成员模板函数定义

Out-of-line member template function definition using another member template function in the signature

提问人:psyill 提问时间:3/14/2017 最后编辑:psyill 更新时间:5/16/2017 访问量:410

问:

我在一些现实生活中的C++11代码中遇到了这个问题,但我把它归结为:

template<int i> struct Dummy {};

template<typename T>
struct Foo {
  template<int i> static constexpr int bar() { return i; }

  template<int i>
  static auto working() -> Dummy<bar<i>()>;

  template<int i>
  static auto also_working() -> Dummy<Foo<T>::template bar<i>()>;

  template<int i>
  static Dummy<Foo<T>::template bar<i>()> not_working();
};

template<typename T> template<int i>
auto Foo<T>::working() -> Dummy<bar<i>()> {
  return Dummy<bar<i>()>{};
}

template<typename T> template<int i>
auto Foo<T>::also_working() -> Dummy<Foo<T>::template bar<i>()> {
  return Dummy<bar<i>()>{};
}

template<typename T> template<int i>
Dummy<Foo<T>::template bar<i>()> Foo<T>::not_working() {
  return Dummy<bar<i>()>{};
}

我试图创建一个模板类的模板成员函数的行外定义,其中函数的签名涉及调用另一个模板成员函数,并从类似函数的东西开始。问题在于定义与声明不符。not_working()

Clang 说:

clang++ -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -std=c++11   -c -o out_of_line.o out_of_line.cc
out_of_line.cc:28:42: error: out-of-line definition of 'not_working' does not match any declaration in 'Foo<T>'
Dummy<Foo<T>::template bar<i>()> Foo<T>::not_working() {
                                         ^~~~~~~~~~~

海湾合作委员会 说:

g++ -Wall -Wextra -pedantic -std=c++11   -c -o out_of_line.o out_of_line.cc
out_of_line.cc:28:34: error: prototype for ‘Dummy<bar<i>()> Foo<T>::not_working()’ does not match any in class ‘Foo<T>’
 Dummy<Foo<T>::template bar<i>()> Foo<T>::not_working() {
                                  ^~~~~~
out_of_line.cc:14:43: error: candidate is: template<class T> template<int i> static Dummy<Foo<T>::bar<i>()> Foo<T>::not_working()
   static Dummy<Foo<T>::template bar<i>()> not_working();
                                       ^~~~~~~~~~~

通过反复试验,我发现使用尾随返回类型,我可以获得与声明匹配的定义,从而产生函数。到达那里后,我意识到由于尾随返回类型的范围发生了变化,我可以取消一些名称限定,从而产生更漂亮的函数。also_working()working()

现在我想知道为什么该函数不起作用,即为什么它的定义与其声明不匹配(我可以对我找到的解决方案一无所知,但我可能会遇到更多此类问题,我不想浪费更多时间使用反复试验);错误是在编译器中还是在我的代码中。我已经通读了 14.6 名称解析 [temp.res],但我不确定适用于这种情况的规则。not_working()

澄清问题:给定 C++11 标准中的规则:

  1. 定义是否应与声明相符?not_working()
  2. 确定 1.?
  3. 规则如何从 2.确定 1.?
C++11 模板 language-lawyer trailing-return-type

评论

0赞 Constructor 3/15/2017
奇怪但真实......使用 C++14 变量模板的相同代码使用 g++ 可以很好地编译,但仍然不能使用 clang++ 编译。对我来说看起来像一个编译器错误。
0赞 psyill 3/15/2017
虽然没有解决这个特定问题,但这个核心语言问题包含了一个答案,表明其目的是让行外定义与文本匹配签名的声明相匹配。
0赞 TBBle 5/11/2017
帮助处理您无法编写的类型的地方之一。据我所知,gcc 和 clang 都很满意,并且会在需要时推断出返回类型。autoauto Foo<T>::working();

答:

0赞 TBBle 5/11/2017 #1

看起来它正在尝试实现 CWG2,但可能正在以令人惊讶的顺序做事。看看 gcc 的错误:

prog.cc:28:34: error: prototype for 'Dummy<bar<i>()> Foo<T>::not_working()' does not match any in class 'Foo<T>'
 Dummy<Foo<T>::template bar<i>()> Foo<T>::not_working() {
                                  ^~~~~~
prog.cc:14:43: error: candidate is: template<class T> template<int i> static Dummy<Foo<T>::bar<i>()> Foo<T>::not_working()
   static Dummy<Foo<T>::template bar<i>()> not_working();
                                           ^~~~~~~~~~~

定义与返回类型一起看到,但候选声明具有返回类型。具体来说,资格已经丧失。Dummy<bar<i>()>Dummy<Foo<T>::bar<i>()>Foo<T>::bar<i>

将 的定义更改为 具有返回类型,我们得到有用的并行错误:also_workingDummy<Foo<T>::template bar<2>()>

prog.cc:23:6: error: prototype for 'Dummy<Foo<T>::bar<2>()> Foo<T>::also_working()' does not match any in class 'Foo<T>'
 auto Foo<T>::also_working() -> Dummy<Foo<T>::template bar<2>()> {
      ^~~~~~
prog.cc:11:15: error: candidate is: template<class T> template<int i> static Dummy<Foo<T>::bar<i>()> Foo<T>::also_working()
   static auto also_working() -> Dummy<Foo<T>::template bar<i>()>;
               ^~~~~~~~~~~~

这里的定义是带有返回类型(如所写)的,候选声明具有返回类型。Dummy<Foo<T>::bar<2>()>Dummy<Foo<T>::bar<i>()>

显然,它甚至与 Foo<T> 的上下文不同,因为从返回类型的声明或定义中删除会使其停止工作。(把两者都拿出来可以让你回来。Foo<T>::bar<i>bar<i>Foo<T>::templatealso_workingworking

我尝试将声明更改为:not_working

  template<int i>
  static Dummy<bar<i>()> not_working();

现在 GCC 抱怨:

prog.cc:28:34: error: prototype for 'Dummy<bar<i>()> Foo<T>::not_working()' does not match any in class 'Foo<T>'
 Dummy<Foo<T>::template bar<i>()> Foo<T>::not_working() {
                                  ^~~~~~
prog.cc:14:26: error: candidate is: template<class T> template<int i> static Dummy<bar<i>()> Foo<T>::not_working()
   static Dummy<bar<i>()> not_working();
                          ^~~~~~~~~~~

这显然是荒谬的,因为一旦编译器完成它,我们就会有字符对字符的可比声明和定义。Dummy<bar<i>()> Foo<T>::not_working()

评论

0赞 psyill 5/16/2017
感谢您与 GCC 的较量,但恕我直言,特定编译器未能将定义与声明匹配这一事实并不能回答这个问题。我一直在寻找所涉及的特定规则,告诉我定义是否应该根据标准与声明匹配,而不管使用什么编译器。重新阅读我的问题,我发现它显然太模糊了:我会更新它以更具体。对不起,如果我的含糊不清误导了你。
0赞 TBBle 5/16/2017
好吧,您已经在评论中得到了答案,即规范中的顺序目前含糊不清,CWG 第 2 期旨在解决这个问题,但尚未措辞和提交。我想回答的是为什么它不起作用。碰巧 gcc 为我们提供了它试图匹配的线索,而 clang 只是在说“不匹配”。