Is an implementation of std::tuple allowed to fail with triggering a derived-to-base conversion for empty class elements?
Asked Answered
S

2

18

This code does not compile with GCC4.7

struct A {};
void f(A);

struct B { B(std::tuple<A>); };
void f(B);

int main() {
  f(std::make_tuple(A()));
}

Because GCC derives from A to make use of the empty base class optimization. However that causes GCC to pick f(A) and complain

error: 'A' is an inaccessible base of 'tuple<A>'

Is this error granted by the C++ Standard or is this simply a bug of libstdc++?

Spann answered 16/12, 2012 at 15:45 Comment(10)
clang32: error: cannot cast 'tuple<typename __decay_and_strip<A>::__type>' to its private base class 'A'Mistletoe
VS compiler is fine with this code.Backcross
compiles and runs fine on clang trunk 3.3Spinal
@chico thanks for the report! does your clang use libc++ or libstdc++?Spann
@JohannesSchaub-litb I used libc++ (also from trunk)Spinal
@chico i see now. libc++ has the actual data as a member of the std::tuple object, so that there's no spurious conversion.Spann
@LeonidVolnitsky: I see no reason to believe this is a compiler issue. There is nothing special in a tuple that require compiler dedicated assistance, though one could defer to the compiler for ease obviously. It seems therefore that this would be a library issue, what is unclear is whether this is prohibited or a "hole" in the Standard (it certainly seem unintended).Inadvertence
reported: gcc.gnu.org/bugzilla/show_bug.cgi?id=55713Spann
@JohannesSchaub-litb: furthermore, it seems that a conforming implementation can still use EBO quite easily -> just wrap the "derived class" => see liveworkspaceInadvertence
@MatthieuM. this is what libc++ is doing.Spann
W
2

Under clause 17 Library introduction:

17.5.2.3 Private members [objects.within.classes]

1 - Clauses 18 through 30 and Annex D do not specify the representation of classes, and intentionally omit specification of class members. An implementation may define static or non-static class members, or both, as needed to implement the semantics of the member functions specified in Clauses 18 through 30 and Annex D.

This is supported by 1.4 Implementation compliance [intro.compliance]:

3 - For classes and class templates, the library Clauses specify partial definitions. Private members (Clause 11) are not specified, but each implementation shall supply them to complete the definitions according to the description in the library Clauses.

Implementing specified semantics through inheritance is not explicitly discussed anywhere in clause 17, but it is implicitly permitted through paragraph 3 of 17.5.2.3 above:

3 - An implementation may use any technique that provides equivalent external behavior.

This is how, for example, the node-based ordered associative containers can share implementation detail (including, eventually, class members) through inheritance.

Since the external behaviour of tuple is changed between having A as a class member and directly inheriting it, and since this change of behaviour causes the rejection of otherwise well-formed programs (as opposed to just changing the sizeof of a class), libstdc++ is in violation of the Standard.

Weeny answered 17/12, 2012 at 10:45 Comment(0)
I
3

I would say no.

At the very least:

§20.4.1 [tuple.general]

1/ [...] An instantiation of tuple with two arguments is similar to an instantiation of pair with the same two arguments. See 20.3.

And yet:

#include <tuple>

struct A {};
void f(A);

struct B { B(std::tuple<A, A>); };
void f(B);

int main() {
  f(std::make_tuple(A(), A()));
}

fails with:

Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:10:30: error: 'A' is an ambiguous base of 'std::tuple<A, A>'
source.cpp:4:6: error: initializing argument 1 of 'void f(A)'

And I very much doubt this was the Standard's intent.

Still, the least one can say is that §20.4 is rather succinct...

Inadvertence answered 16/12, 2012 at 16:7 Comment(0)
W
2

Under clause 17 Library introduction:

17.5.2.3 Private members [objects.within.classes]

1 - Clauses 18 through 30 and Annex D do not specify the representation of classes, and intentionally omit specification of class members. An implementation may define static or non-static class members, or both, as needed to implement the semantics of the member functions specified in Clauses 18 through 30 and Annex D.

This is supported by 1.4 Implementation compliance [intro.compliance]:

3 - For classes and class templates, the library Clauses specify partial definitions. Private members (Clause 11) are not specified, but each implementation shall supply them to complete the definitions according to the description in the library Clauses.

Implementing specified semantics through inheritance is not explicitly discussed anywhere in clause 17, but it is implicitly permitted through paragraph 3 of 17.5.2.3 above:

3 - An implementation may use any technique that provides equivalent external behavior.

This is how, for example, the node-based ordered associative containers can share implementation detail (including, eventually, class members) through inheritance.

Since the external behaviour of tuple is changed between having A as a class member and directly inheriting it, and since this change of behaviour causes the rejection of otherwise well-formed programs (as opposed to just changing the sizeof of a class), libstdc++ is in violation of the Standard.

Weeny answered 17/12, 2012 at 10:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.