Hi, The following program is wrong, but it is accepted by clang. It's an attempt to reproduce at small scale a problem I have with clang++ on a much larger program, but the fixed version of the following program unfortunately (for me) does not show the failure I was trying to reproduce. There are three classes. The base class roughly represents some parametric container that will be parameterized by a tuple<char, char>. The middle class aggregates an instance of the base class, and only forwards the calls to this base class. The derived class specializes the decorator by looking at the first item of the tuple only. This program is invalid, because the definition of word as "std::tuple_element<Tape, Word>" is missing the "::type" bit. Yet clang accepts this program, and incorrectly calls a routine which requires a tuple<char, char> with an effective argument which is just a char. $ cat /tmp/bar.cc #include <string> #include <iostream> #include <tuple> template <typename Word> struct base { using word = std::tuple<char, char>; int out(int s) { return s; } int out(int s, word w) { std::cerr << "Tuple: " << std::get<0>(w) << std::get<1>(w) << '\n'; return s; } }; template <typename Aut> struct decorator { decorator(Aut a) : a_(a) {} Aut a_; template <typename... Args> auto out(Args&&... args) -> decltype(a_.out(args...)) { return a_.out(args...); } }; template <size_t Tape, typename Word> struct derived : decorator<base<Word>> { using super_t = decorator<base<Word>>; using word = std::tuple_element<Tape, Word>; using super_t::out; using super_t::super_t; int out(int s, word w) { std::cerr << "char: " << w << '\n'; return s; } }; int main() { using word = std::tuple<char, char>; base<word> b; derived<0, word> d(b); d.out(12, 'a'); } $ clang++-mp-3.5 --version clang version 3.5.0 (trunk 210448) Target: x86_64-apple-darwin13.2.0 Thread model: posix $ clang++-mp-3.5 -std=c++11 bar.cc $ ./a.out Tuple: a $
See also #20175 which properly features the failure I was trying to reproduce.
It looks like Clang is behaving correctly here? This: d.out(12, 'a'); considers the following functions: 1) int derived::out(int s, word w) [word = std::tuple_element<0, std::tuple<char, char>>] 2) auto decorator<Aut>::out(Args&&... args) -> decltype(a_.out(args...)) [Aut = base<std::tuple<char,char>>, Args = {int, char}] The first one doesn't work. The second one does work, and calls base<std::tuple<char, char>>::out(int, word) passing in 12 and 'a'. This constructs a std::tuple<char, char>('a'), that is, std::tuple<char, char>('a', '\0'). If you disagree, please reopen this bug and explain what you think should happen here and why.
Hi Richard, Thanks for your detailed comments. So it all boils down to the behavior of std::tuple's constructor, which in the case of libc++, does not require the same number of effective arguments as the size of the tuple. #include <tuple> int main() { std::tuple<int, int> t1(1); } I don't have the final C++11 standard, just a copy of the last draft, but unless it changed quite late, I don't see how this is valid. 20.4.2.1 line 8 states that both parameter packs must have the same size. I agree then that this would be rather a libc++ issue. Do you agree?
Hi Akim, libc++'s tuple accepts that code as an extension. Your correct that the standard requires that the size of the parameter pack is the size of the tuple. However it does not say that the overload is removed from overload resolution when that is not the case. The relevant section from the C++11 standard. > template <class... UTypes> > explicit tuple(UTypes&&... u); > 7 Requires: sizeof...(Types) == sizeof...(UTypes). > is_constructible<Ti, Ui&&>::value is true for all i. > 8 Effects: Initializes the elements in the tuple with the corresponding value > in std::forward<UTypes>(u). > 9 Remark: This constructor shall not participate in overload resolution > unless each type in UTypes is implicitly convertible to its > corresponding type in Types.
(In reply to comment #4) > > 7 Requires: sizeof...(Types) == sizeof...(UTypes). > > is_constructible<Ti, Ui&&>::value is true for all i. Since this was violated, 17.6.4.11/1 applies: "Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated." ... so we're permitted (and, as far as I can tell, required) to accept this tuple construction, and libc++'s extension is a conforming one. If you think that this tuple constructor should not participate in overload resolution if it has an arity mismatch, please file a defect report against the C++ standard library. Mail lwgchair@gmail.com for that. (Resolving 'MOVED'.)