5
votes

This is my binary operator to concatenate tuples:

template <class... Args1, class... Args2>
constexpr decltype(auto) operator+(const std::tuple<Args1...> &tup1,
                                   const std::tuple<Args2...> &tup2) {
   return std::tuple_cat(tup1, tup2);
}

It works perfectly on both compiler (gcc, clang) with two tuples:

template <class Arg1, class Arg2>
constexpr decltype(auto) concat_test(Arg1 &&arg1, Arg2 &&arg2) {
   return arg1 + arg2;
}

But when I try to use it in fold expression like follows:

template <class... Args>
constexpr decltype(auto) multiple_concat(Args &&... args) {
   return (args + ...);
}

gcc 7.1.1 compiles it without any errors, unlike clang 5.0, which produces error output:

error: call to function 'operator+' that is neither visible in the template definition nor found by argument-dependent lookup

return (args + ...);

note: in instantiation of function template specialization 'multiple_concat < std::__1::tuple &, std::__1::tuple &>' requested here

multiple_concat(tup1, tup2);

note: 'operator+' should be declared prior to the call site

constexpr decltype(auto) operator+(const std::tuple &tup1, const std::tuple &tup2)

Is this code ill-formed and what exactly is clang talking about?

3
clang 4.0.1 compiles this, as does clang 6.0. It's highly likely a compiler bug.Rakete1111
@Rakete1111 my clang 4.0.1 (tags/RELEASE_401/final) doesn't compile this, as well as clang 6.0 ExampleМаксим Шапошников
I'm using xcode clang 9.0.0 (roughly equivalent to the open-source clang 4.0), and it also doesn't compile. I met this problem myself in a different context, which led me to this post.Ainz Titor
Does this answer your question? fold expression and function name lookupDavis Herring

3 Answers

1
votes

Since the other answers don’t come out and say this: the code is fine. This is a longstanding Clang bug, affecting versions through 11.

0
votes

Aug 2018: Xcode 9.0 (roughly equivalent to open-source clang 4.0) still does not compile this code, while g++ does the job correctly.

I know it's painful not to be able to use the shiny new template fold syntax, but here's a workaround based on if constexpr, the next best thing we can use.

template <typename T, typename... Ts>
constexpr decltype(auto) multiple_concat(T&& arg, Ts&&... rest) {
    if constexpr (sizeof ...(rest) == 0) {
        return arg;
    }
    else {  // recursively concatenate the tuple
        return arg + multiple_concat(std::forward<Ts>(rest) ...);
    }
}

Clang happily compiles this code.

0
votes

Apparently, unqualified lookup fails. This one makes it compile with Clang 6:

namespace std {
    template <class... Args1, class... Args2>
    constexpr decltype(auto) operator+(const ::std::tuple<Args1...> &tup1,
                                       const ::std::tuple<Args2...> &tup2) {
      return ::std::tuple_cat(tup1, tup2);
    }

    template <class... Args1, class... Args2>
    constexpr decltype(auto) operator+(::std::tuple<Args1...> &&tup1,
                                       ::std::tuple<Args2...> &&tup2) {
      return ::std::tuple_cat(tup1, tup2);
    }
}