8
#include <string>
#include <iostream>
#include <tuple>
#include <utility>


template<typename... T> struct test {

    using args_type = std::tuple<T...>;

    args_type x;

    template<std::size_t... I>
    void callme(std::index_sequence<I...>) {
        int _[] = {(std::get<I>(x).std::tuple_element<I, args_type>::type::~type(), true)...};
    }
};

int main() {
}

The error message is

clang-3.7  -std=gnu++1y  -Wc++14-extensions test.cpp
test.cpp:15:56: error: expected ')'
        int _[] = {(std::get<I>(x).std::tuple_element<I, args_type>::type::~type(), true)...};
                                                       ^
test.cpp:15:20: note: to match this '('
        int _[] = {(std::get<I>(x).std::tuple_element<I, args_type>::type::~type(), true)...};
                   ^
1 error generated.

The same code seems to compile just fine with G++ 4.9.2. I couldn't find any relevant bug report on Clang yet.

5
  • 1
    Where did this copy of clang 3.7 come from? The most recent release of LLVM is only 3.6. Commented May 12, 2015 at 21:11
  • 1
    Does it compile with 3.6? Commented May 12, 2015 at 21:13
  • @Qix nope, it doesn't Commented May 12, 2015 at 21:14
  • @Qix, it failed to compile in 3.5, 3.6, 3.7-head Commented May 12, 2015 at 21:48
  • .std::tuple_element<I, args_type>::type::~type() is supposed to call the destructor of the element? How is ::type not a dependent type there? Commented May 13, 2015 at 18:33

2 Answers 2

10

Appears to be a Clang bug, though the lookup of such pseudo-destructor-names is probably defected and subject of open CWG issues, specifically 555 and 399.

The significant bit of the expansion pattern is

std::get<I>(x).std::tuple_element<I, args_type>::type::~type()

Here, the bit between . and () is a pseudo-destructor-name; Qualified name lookup then mandates that

If a pseudo-destructor-name (5.2.4) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier. Similarly, in a qualified-id of the form:

        nested-name-specifieropt  class-name :: ~ class-name

the second class-name is looked up in the same scope as the first.

I.e. type is looked up in std::tuple_element<I, args_type>, where it's found to refer to some type. Note that class-name is a grammatical name for identifiers (and simple-template-ids), and need not refer to an actual class. std::get<I>(x).std::tuple_element<I, args_type>::type::~type then refers to the destructor of type.

Workaround with an auxiliary function:

template <typename T>
void destroy(T& p) {p.~T();}

template<typename... T> struct test {
    using args_type = std::tuple<T...>;

    args_type x;

    template<std::size_t... I>
    void callme(std::index_sequence<I...>) {
        int _[] = {(destroy(std::get<I>(x)), 0)...};
    }
};
Sign up to request clarification or add additional context in comments.

7 Comments

I would file a report. That's odd.
The specification for destructor name lookup is all sorts of messed up. There's at least one longstanding core issue about it.
@T.C. I just realized how weird this really is.
@Columbo 399, too. Also, this one is not necessarily a pseudo-destructor. Could be a real one.
Is the code actually complied with C++14 specification though i.e. it's actually GCC bug? (open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.pdf section 5.2.4 page 100)
|
0

Interestingly, hstong from IBM mentioned a couple workarounds that work better.

int _[] = {(std::get<I>(x).::std::tuple_element<I, args_type>::type::~type(), true)...};

or

int _[] = {(std::get<I>(x).std::template tuple_element<I, args_type>::type::~type(), true)...};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.