18

The Standard says that std::tuple has the following member functions

constexpr tuple();
explicit tuple(const Types&...);

Can someone please explain what is supposed to happen for std::tuple<>?

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 3
    `std::tuple<>` isn't an expression. what's the question? – Cheers and hth. - Alf Jul 08 '12 at 19:39
  • 4
    @Cheersandhth.-Alf why should it be an expression. It's a class type. The constructor declarations will conflict and raise an error. How is this solved? – Johannes Schaub - litb Jul 08 '12 at 19:42
  • 4
    In other words, it would seem that the default constructor is ambiguous? – Vaughn Cato Jul 08 '12 at 19:47
  • 1
    @johannes: post this as a defect to [comp.std.c++], pluh-ease. – Cheers and hth. - Alf Jul 08 '12 at 20:02
  • 6
    There's two relevant open cwg issues: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1395 and http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1432 – Howard Hinnant Jul 08 '12 at 20:19
  • @Howard but in this case, we have two non-template constructors declared in the class. It is the equivalent of `struct tuple { constexpr tuple(); explicit tuple(); };`. It's not the call that is ambiguous, but the repeated declaration itself is ill-formed (and is the default constructor constexpr or explicit or both?). – Johannes Schaub - litb Jul 08 '12 at 20:50
  • 1
    Johannes and this thread have been cited in this accepted paper: N4387: Improving Pair and Tuple (Revision 3): http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4387 See section Discussion, point 4. – underscore_d Sep 11 '16 at 08:02

3 Answers3

6

I guess the definition given in the standard is supposed to be pseudocode. That is the case with many of the definitions in the standard; it contains several requirements that are given verbally, but are satisfiable only with tricks like enable_if. This seems to be an example where the C++-like pseudocode notation can actually lead to illegal C++ when trying to instantiate such an empty tuple (or it might just be an omission).

Both stdlibc++ and libc++ have an explicit specialization for the zero-element tuple. For example, in stdlibc++:

  // Explicit specialization, zero-element tuple.
  template<>  
    class tuple<>
    {
    public:
      void swap(tuple&) noexcept { /* no-op */ }
    };

with an implicitly-defined unambiguous default constructor.

Libc++ does not explicitly declare the parameterless default constructor. Presumably the templated constructor is then chosen as default constructor for non-empty tuples.

Interestingly, the two libraries disagree on what members the empty tuple has. For example, the following compiles with libc++, but not with libstdc++:

#include <tuple>
#include <memory>

int main() {
  std::tuple<> t(std::allocator_arg, std::allocator<int>());
}
Philipp
  • 48,066
  • 12
  • 84
  • 109
  • I've tried to find it but was not successful. Can you please point me to its specification? – Johannes Schaub - litb Jul 08 '12 at 20:02
  • One would expect that in any real implementation, but I don't see it in the Holy Standard. – Cheers and hth. - Alf Jul 08 '12 at 20:02
  • 1
    I'm not sure I agree with your edit. The standard's tuple class declaration is perfectly valid C++, so long as it is not instantiated with an empty template argument list, is it not? –  Jul 08 '12 at 20:16
  • @JohannesSchaub-litb: It's not in the standard (either an omission, or it's regarded as an implementation detail), but it seems there is no other way if you want to implement the standard library in C++. – Philipp Jul 08 '12 at 20:16
  • From that I'd say that in standard C++11 you simply cannot declare a `tuple<>` of size 0. Thus, that explicit specialization from [std]libc++ should be considered an _extension_ to the standard library, at least for now. – rodrigo Jul 08 '12 at 20:19
  • @rodrigo Also, the standard (§20.4.1/1) mentions "the class template tuple that can be instantiated with any number of arguments." where "any" presumably includes zero arguments. – Philipp Jul 08 '12 at 20:22
  • 3
    Another standard quote (§20.4.2.7/5): "For any two *zero-length tuples* e and f, e < f returns false." – Philipp Jul 08 '12 at 20:25
  • 1
    @hvd: Actually, there is a reference to `tuple<>()` in a rather obscure (for me) example of `std::scoped_allocator_traits::construct()` at § 20.12.4-12. Anyway, I would expect it added in a future revision of the library, although I cannot find any defect report in the _C++ Standard Library Issues List_. – rodrigo Jul 08 '12 at 20:28
  • _"Presumably the templated constructor is then chosen as default constructor for non-empty tuples"_: If I understand explicit specialization correctly, `tuple<>`, as presented, has no user defined constructors at all, so the compiler-generated default constructor is used. – rodrigo Jul 08 '12 at 20:34
  • @rodrigo Thanks for that, that's one my search didn't pick up on. –  Jul 08 '12 at 20:34
  • @rodrigo: I'm worried about which default constructor gets called for non-empty tuples in libc++. It has lots of user-defined constructors, but no parameterless one. So I think the default constructor in that case is `template> explicit tuple(_Up&&...)`. – Philipp Jul 08 '12 at 20:37
  • @Philipp: Oh! You said _non-_ empty tuples! Anyway, in [the latest version](http://llvm.org/svn/llvm-project/libcxx/trunk/include/tuple) of the header there is a user defined default construtor just as the very first member of the class. – rodrigo Jul 08 '12 at 20:45
3

I believe this is a minor error in the standard. Clearly, when the Types parameter pack is empty, the two constructor calls are equivalent and cannot be overloaded (see C++11 section 13). (Further note that the constructor using Types is not a member template either --if it was, then it would be a legal overload.).

In other words, this code will not compile:

template <typename... Types>
struct Test
{
  constexpr Test() {}
  explicit Test(Types const&...) { /* etc. */ }
};

int main()
{
  Test<> a;
  Test<int> b;
}

e.g., a g++ v4.8 snapshot outputs:

tt.cxx: In instantiation of ‘struct Test<>’:
tt.cxx:10:10:   required from here
tt.cxx:5:12: error: ‘Test<Types>::Test(const Types& ...) [with Types = {}]’ cannot be overloaded
   explicit Test(Types const&...) { /* etc. */ }
            ^
tt.cxx:4:13: error: with ‘constexpr Test<Types>::Test() [with Types = {}]’
   constexpr Test() {}
             ^

This can be fixed by using partial specialization:

template <typename... Types>
struct Test
{
  constexpr Test() {} // default construct all elements
  explicit Test(Types const&...) { /* etc. */ }
  // and all other member definitions
};

template <>
struct Test<>
{
  constexpr Test() {}
  // and any other member definitions that make sense with no types
};

int main()
{
  Test<> a;
  Test<int> b;
}

which will compile correctly.

It appears the standard wanted a constexpr default constructor was so that std::tuple<> var; could be written instead of writing std::tuple<> var(); or std::tuple<> var{}; because of the use of explicit with the other constructor. Unfortunately, its definition of std::tuple does not work for tuples of size zero. The standard does permit such in section 20.4.2.7 (relational operators) though, "For any two zero-length tuples, [...]". Oops! :-)

Paul Preney
  • 1,283
  • 11
  • 10
  • lol, who downvoted this? it's correct by my understanding of this accepted paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4387 see Discussion, point 4 - which cites this very thread and Johannes by name. – underscore_d Sep 11 '16 at 07:59
-1

At first sight, the ambiguity would only matter at the point where it's called, and then you have normal overload resolution.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • The first sight is incorrect. Declaring a default constructor with zero parameters two times in a class is invalid. Even if it somehow worked, it would be ambiguous in overload resolution :) – Johannes Schaub - litb Jul 08 '12 at 19:53
  • @JohannesSchaub-litb: In normal classes. Templates are another matter, as members of a class template are only instantiated as needed. In general the compiler can't easily determine whether two methods of a template would be ambiguous before instantiation. – MSalters Jul 08 '12 at 20:30
  • ... and while I was trying to figure out the exact overload resolution rules for parameter packs, Howard Hinnant listed the open issues on them. – MSalters Jul 08 '12 at 20:43
  • No, that is not true. The declaration of all memberfunctions and all datamembers are instantiated when the enclosing class template is. – Johannes Schaub - litb Jul 08 '12 at 20:55
  • Ah yes, the declarations are. (I'm assuming implicit instantiation, 14.7.1 - your question is a bit short on code) – MSalters Jul 08 '12 at 21:12