Using this and attributes in member function trailing return types?

2.2k views Asked by At

In this answer I gave, it made sense to use this and the attribute of the class _arg in the trailing return type as part of the decltype expression. It's possible to do without, but inconvenient.

Neither clang 3.0 (see below) nor gcc 4.5.2 accepted it though.

#include <iostream>

class MyClass {
public:
  MyClass(int i): _arg(i) {}

  template <typename F>
  auto apply(F& f) -> decltype(f(_arg)) {
    return f(_arg);
  }

  template <typename F>
  auto apply(F& f) -> decltype(f(*this, _arg)) {
    return f(*this, _arg);
  }

private:
  int _arg;
};

struct Id {
  template <typename V>
  V operator()(V v) const { return v; }
};

struct ComplexId {
  template <typename C, typename V>
  V operator()(C const&, V v) { return v + 1; }
};

int main() {
  Id id; ComplexId complex;

  MyClass c(0);

  std::cout << c.apply(id) << " " << c.apply(complex) << "\n";
}

clang 3.0 says:

$ clang++ -std=c++11 -Weverything test.cpp
test.cpp:8:38: error: use of undeclared identifier '_arg'
      auto apply(F& f) -> decltype(f(_arg)) {
                                     ^
test.cpp:8:45: error: type name requires a specifier or qualifier
      auto apply(F& f) -> decltype(f(_arg)) {
                                            ^
test.cpp:8:45: error: C++ requires a type specifier for all declarations
      auto apply(F& f) -> decltype(f(_arg)) {
                          ~~~~~~~~          ^
test.cpp:8:7: error: 'auto' return without trailing return type
      auto apply(F& f) -> decltype(f(_arg)) {
      ^
test.cpp:13:39: error: invalid use of 'this' outside of a nonstatic member function
      auto apply(F& f) -> decltype(f(*this, _arg)) {
                                      ^
test.cpp:13:52: error: type name requires a specifier or qualifier
      auto apply(F& f) -> decltype(f(*this, _arg)) {
                                                   ^
test.cpp:13:52: error: C++ requires a type specifier for all declarations
      auto apply(F& f) -> decltype(f(*this, _arg)) {
                          ~~~~~~~~                 ^
test.cpp:13:7: error: 'auto' return without trailing return type
      auto apply(F& f) -> decltype(f(*this, _arg)) {
      ^
8 errors generated.

Hum... not so great.

However, the support of C++11 is hacky at best in most compilers and I could not find specific restrictions mentionned in the Standard (n3290).

In the comments, Xeo suggested that it might have been a defect in the Standard...

So, is this allowed or not ?

Bonus: and do more recent versions of clang / gcc support this ?

3

There are 3 answers

0
Xeo On BEST ANSWER

I misremembered. It was a defect at some point, but was eventually resolved and voted into the FDIS.

§5.1.1 [expr.prim.general]

If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X” between the optional cv-qualifer-seq and the end of the function-definition, member-declarator, or declarator.

As such, Clang and GCC just don't implement it correctly yet.

struct X{
  // 'this' exists between the | markers
  void f() const volatile | {
  } |
  auto g() const volatile | -> void {
  } |
};
5
Johannes Schaub - litb On

Your code is invalid C++11, because the class is not consider complete in the return type of a member function. You can only access members that have previously been declared. Like so

class MyClass {
private:
  int _arg;

public:
  MyClass(int i): _arg(i) {}

  template <typename F>
  auto apply(F& f) -> decltype(f(_arg)) {
    return f(_arg);
  }

  template <typename F>
  auto apply(F& f) -> decltype(f(*this, _arg)) {
    return f(*this, _arg);
  }
};

Modulo that, yes using this is valid in C++11 in a trailing return type.

1
Bruno De Fraine On

I don't know if what you write is legal, but there are some other ways of achieving what you want:

  template <typename F>
  auto apply(F& f) -> decltype(f(*(MyClass*)0, (int)0)) {
    return f(*this, _arg);
  }

Or:

  template <typename F>
  typename std::result_of<F(MyClass,int)>::type apply(F& f) {
    return f(*this, _arg);
  }