Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Clang++ compiler fail to compile the following variadic template code?

Tags:

c++

c++14

clang++

#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.

like image 610
Nat Avatar asked May 12 '15 21:05

Nat


1 Answers

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)...};
    }
};
like image 158
Columbo Avatar answered Nov 15 '22 19:11

Columbo