Incomplete types in template code - c++

Suppose we have two types (complete and incomplete):
struct CompleteType{};
struct IncompleteType;
Also we have template code:
#include <type_traits>
template <typename = X(T)>
struct Test : std::false_type {};
template <>
struct Test<T> : std::true_type {};
T can be CompleteType or IncompleteType here and X(T) can be T, decltype(T()) or decltype(T{}) (suppose X(T) is a macro).
This code is used in the following manner:
std::cout << std::boolalpha << Test<>::value << std::endl;
Below you can see how different compilers deal with such code:
clang 3.4
X(T) \ T CompleteType IncompleteType
T true true
decltype(T()) true --- (1, 2)
decltype(T{}) true --- (1, 2)
error: invalid use of incomplete type 'IncompleteType' is given even on template class declarations with incomplete types (both for decltype(T()) and decltype(T{}), but not for simple T) without using Test<>::value in the code.
error: too few template arguments for class template 'Test'
g++ 4.8.1
X(T) \ T CompleteType IncompleteType
T true true
decltype(T()) true true
decltype(T{}) true true
vc++ 18.00.21005.1
X(T) \ T CompleteType IncompleteType
T true true
decltype(T()) true --- (1)
decltype(T{}) true --- (2)
error C2514: 'IncompleteType' : class has no constructors
error C2440: '<function-style-cast>' : cannot convert from 'initializer-list' to 'IncompleteType' Source or target has incomplete type
What compiler acts in accordance with standard? Note that simple string like std::cout << typeid(X(IncompleteType)).name() << std::endl; does not compile on all compilers for all variants of X (except for vc++ and X(T) == T).

I believe that the behavior of Clang and MSVC are consistent with the standard in this situation. I think GCC is taking a bit of a short-cut here.
Let's put a few facts on the table first. The operand of a decltype expression is what is called an unevaluated operand, which are treated a bit differently due to fact that they are ultimately never evaluated.
Particularly, there are fewer requirements about the types being complete. Basically, if you have any temporary object (as parameters or return values in the functions or operators involved in the expression), they are not required to be complete (see Sections 5.2.2/11 and 7.1.6.2/5). But this only lifts the usual restriction of "you cannot declare an object of an incomplete type", but it does not lift the other restriction on incomplete types, which is that "you cannot call a member function of an incomplete type". And that's the kicker.
The expression decltype(T()) or decltype(T{}), where T is incomplete, must necessarily look-up the constructor(s) of the type T, as it's a (special) member function of that class. It's only the fact that it's a constructor call that creates a bit of an ambiguity (i.e., Is it just creating a temporary object? Or is it calling a constructor?). If it was any other member function, there would be no debate. Fortunately, the standard does settle that debate:
12.2/1
Even when the creation of the temporary object is unevaluated (Clause
5) or otherwise avoided (12.8), all the semantic restrictions shall
be respected as if the temporary object had been created and later
destroyed. [ Note: even if there is no call to the destructor or
copy/move constructor, all the semantic restrictions, such as
accessibility (Clause 11) and whether the function is deleted
(8.4.3), shall be satisfied. However, in the special case of a
function call used as the operand of a decltype-specifier (5.2.2), no
temporary is introduced, so the foregoing does not apply to the
prvalue of any such function call. - end note ]
The last sentence might be a bit confusing, but that only applies to the return-value of a function call. In other words, if you have T f(); function, and you declare decltype(f()), then T is not required to be complete or have any semantic checks on whether there is a constructor / destructor available and accessible for it.
In fact, this whole issue is exactly why there is a std::declval utility, because when you cannot use decltype(T()), you can just use decltype(std::declval<T>()), and declval is nothing more than a (fake) function that returns a prvalue of type T. But of course, declval is intended to be used in less trivial situations, such as decltype( f( std::declval<T>() ) ) where f would be a function taking an object of type T. And declval does not require that the type is complete (see Section 20.2.4). This is basically the way you get around this whole problem.
So, as far as GCC's behavior is concerned, I believe that it takes a short-cut as it attempts to figure out what the type of T() or T{} is. I think that as soon as GCC finds that T refers to a type name (not a function name), it deduces that this is a constructor call, and therefore, regardless of what the look-up finds as the actual constructor being called, the return type will be T (well, strictly speaking constructors don't have a return type, but you understand what I mean). The point here is that this could be a useful (faster) short-cut in an unevaluated expression. But this is not standard-compliant behavior, as far as I can tell.
And if GCC allows for CompleteType with the constructor either deleted or private, then that is also in direct contradiction with the above-quoted passage of the standard. The compiler is required to enforce all semantic restrictions in that situation, even if the expression is not evaluated.
Note that simple string like std::cout << typeid(X(IncompleteType)).name() << std::endl; does not compile on all compilers for all variants of X (except for vc++ and X(T) == T).
This is expected (except for MSVC and X(T) == T). The typeid and sizeof operators are similar to decltype in the sense that their operands are unevaluated, however, both of them have the additional requirement that the type of the resulting expression must be a complete type. It is conceivable that a compiler could resolve typeid for incomplete types (or at least, with partial type-info), but the standard requires a complete type such that compilers don't have to do this. I guess this is what MSVC is doing.
So, in this case, the T() and T{} cases fail for the same reason as for decltype (as I just explained), and the X(T) == T case fails because typeid requires a complete type (but MSVC manages to lift that requirement). And on GCC, it fails due to typeid requiring a complete type for all the X(T) cases (i.e., the short-cut GCC takes doesn't affect the outcome in the case of sizeof or typeid).
So, all in all, I think that Clang is the most standard-compliant of the three (not taking short-cuts or making extensions).

Related

Why does std::is_invocable_r reject functions returning non-moveable types?

I'm curious about the definition of std::is_invocable_r and how it interacts with non-moveable types. Its libc++ implementation under clang in C++20 mode seems to be wrong based on my understanding of the language rules it's supposed to emulate, so I'm wondering what's incorrect about my understanding.
Say we have a type that can't be move- or copy-constructed, and a function that returns it:
struct CantMove {
CantMove() = default;
CantMove(CantMove&&) = delete;
};
static_assert(!std::is_move_constructible_v<CantMove>);
static_assert(!std::is_copy_constructible_v<CantMove>);
CantMove MakeCantMove() { return CantMove(); }
Then it's possible to call that function to initialize a CantMove object (I believe due to the copy elision rules):
CantMove cant_move = MakeCantMove();
And the type traits agree that the function is invocable and returns CantMove:
using F = decltype(MakeCantMove);
static_assert(std::is_invocable_v<F>);
static_assert(std::is_same_v<CantMove, std::invoke_result_t<F>>);
But std::is_invocable_r says it's not possible to invoke it to yield something convertible to CantMove, at least in C++20 under clang:
static_assert(!std::is_invocable_r_v<CantMove, F>);
The definition of std::is_invocable_r is
The expression INVOKE<R>(declval<Fn>(), declval<ArgTypes>()...) is well-formed when treated as an unevaluated operand
with INVOKE<R> being defined as
Define INVOKE<R>(f, t1, t2, …, tN) as [...] INVOKE(f, t1, t2, …, tN) implicitly converted to R.
and INVOKE defined (in this case) as simply MakeCantMove(). But the definition of whether an implicit conversion is possible says:
An expression E can be implicitly converted to a type T if and only if the declaration T t=E; is well-formed, for some invented temporary variable t ([dcl.init]).
But we saw above that CantMove cant_move = MakeCantMove(); is accepted by the compiler. So is clang wrong about accepting this initialization, or is the implementation of std::is_invocable_r_v wrong? Or is my reading of the standard wrong?
For the record, the reason I care about this question is that types like std::move_only_function (I'm using an advanced port to C++20 of this) have their members' overload sets restricted by std::is_invocable_r_v, and I'm finding that it's not possible to usefully work with functions that return a no-move type like this. Is that by design, and if so why?
So is clang wrong about accepting this initialization, or is the
implementation of std::is_invocable_r_v wrong?
This is a bug of libc++. In the implementation of is_invocable_r, it uses is_convertible to determine whether the result can be implicitly converted to T, which is incorrect since is_convertible_v<T, T> is false for non-movable types, in which case std::declval adds an rvalue reference to T.
It is worth noting that both libstdc++ and MSVC-STL have bug reports about this issue, which have been fixed.

Construction of lambda object in case of specified captures in C++

Starting from C++20 closure types without captures have default constructor, see https://en.cppreference.com/w/cpp/language/lambda:
If no captures are specified, the closure type has a defaulted default constructor.
But what about closure types that capture, how can their objects be constructed?
One way is by using std::bit_cast (provided that the closure type can be trivially copyable). And Visual Studio compiler provides a constructor for closure type as the example shows:
#include <bit>
int main() {
int x = 0;
using A = decltype([x](){ return x; });
// ok everywhere
constexpr A a = std::bit_cast<A>(1);
static_assert( a() == 1 );
// ok in MSVC
constexpr A b(1);
static_assert( b() == 1 );
}
Demo: https://gcc.godbolt.org/z/dnPjWdYx1
Considering that both Clang and GCC reject A b(1), the standard does not require the presence of this constructor. But can a compiler provide such constructor as an extension?
But what about closure types that capture, how can their objects be constructed?
You can't. They can only be created from the lambda expression.
And no, bit_cast does not "work everywhere". There is no rule in the C++ standard which requires that any particular lambda type must be trivially copyable (or the same size as its capture member for that matter). The fact that no current implementations break your code does not mean that future implementations cannot.
And it definitely won't work if you have more than one capture member.
Just stop treating lambdas like a cheap way to create a type. If you want to make a callable type with members that you can construct, do that:
#include <bit>
int main() {
struct A
{
int x = 0;
constexpr auto operator() {return x;}
};
// ok everywhere
constexpr A b(1);
static_assert( b() == 1 );
}
Since this is tagged language-lawyer, here's what the C++ standard has to say about all this.
But what about closure types that capture, how can their objects be constructed?
The actual part of the standard that cppreference link is referencing is [expr.prim.lambda.general] - 7.5.5.1.14:
The closure type associated with a lambda-expression has no default constructor if the lambda-expression has a lambda-capture and a defaulted default constructor otherwise. It has a defaulted copy constructor and a defaulted move constructor ([class.copy.ctor]). It has a deleted copy assignment operator if the lambda-expression has a lambda-capture and defaulted copy and move assignment operators otherwise ([class.copy.assign]).
However, clauses 1 and 2 say:
The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.
The closure type is not an aggregate type. An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
[unrelated stuff]
Which means that (apart from the unrelated exceptions), the described interface of the lambda as stated is exhaustive. Since no other constructors than the default one is listed, then that's the only one that is supposed to be there.
N.B. : A lambda may be equivalent to a class-based functor, but it is not purely syntactical sugar. The compiler/implementation does not need a constructor in order to construct and parametrize the lambda's type. It's just programmers who are prevented from creating instances by the lack of constructors.
As far as extensions go:
But can a compiler provide such constructor as an extension?
Yes. A compiler is allowed to provide this feature as an extension as long as all it does is make programs that would be ill-formed functional.
From [intro.compliance.general] - 4.1.1.8:
A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this document. Having done so, however, they can compile and execute such programs.
However, for the feature at hand, MSVC would be having issues in its implementation as an extension:
It should be emmiting a diagnostic.
By its own documentation, it should refuse the code when using /permissive-. Yet it does not.
So it looks like MSVC is, either intentionally or not, behaving as if this was part of the language, which is not the case as far as I can tell.

Type deduction in switch and SFINAE, gcc vs clang [duplicate]

The problem originally arose in this question. Consider the following code:
class Var
{
public:
operator int () const
{ return 0; }
template <typename T>
operator T () const
{ return T(); }
};
int main()
{
Var v;
switch (v)
{ }
}
Without the operator int() const { return 0; }, both g++ and clang reject the code.
However, the above code, with the operator int(), is accepted by clang but rejected by g++ with the following error:
main.cpp:17:14: error: default type conversion can't deduce template argument for 'template<class T> Var::operator T() const'
switch (v)
^
Which compiler is correct?
I believe clang is correct here.
We can see from the draft C++ standard section 6.4.2 The switch statement that this involves a contextually implicit conversion. Paragraph 2 says (*emphasis mine going forward):
The condition shall be of integral type, enumeration type, or class
type. If of class type, the condition is contextually implicitly
converted (Clause 4) to an integral or enumeration type.
We can see the section we need to use is 4 Standard conversions and paragraph 5 covers these cases, it says:
Certain language constructs require conversion to a value having one
of a specified set of types appropriate to the construct. An
expression e of class type E appearing in such a context is said to be
contextually implicitly converted to a specified type T and is
well-formed if and only if e can be implicitly converted to a type T
that is determined as follows: E is searched for conversion functions
whose return type is cv T or reference to cv T such that T is allowed
by the context. There shall be exactly one such T.
This does not reference section 8.5 which allows for overload resolution by specifically referring to section 13.3 without allowing overload resolution we can not use:
template <typename T>
operator T () const
and therefore there is no ambiguity.
Note this is different from paragraph 4 which covers bool conversions in contexts of if, while etc... and says (emphasis mine):
Certain language constructs require that an expression be converted to
a Boolean value. An expression e appearing in such a context is said
to be contextually converted to bool and is well-formed if and only if
the declaration bool t(e); is well-formed, for some invented temporary
variable t (8.5).
which specifically allows for overload resolution and refers directly to section 13.3 which covers this. It makes sense that it is allowed since we have a specific destination type bool to convert to which we don't have in the switch case.
Why
We can figure this out by going looking at N3323: A Proposal to Tweak Certain C++ Contextual Conversions, v3 it covers this issue. It would be hard to quote the whole paper so I will attempt to quote enough of the context. It says:
The context in which a C++ expression appears often influences how the
expression is evaluated, and therefore may impose requirements on the
expression to ensure such evaluation is possible. [...]
In four cases, the FDIS (N3290) uses different language to specify an
analogous contextdependent conversion. In those four contexts, when an
operand is of class type, that type must have a “single non-explicit
conversion function” to a suitable (context-specific) type. [...]
and includes:
[stmt.switch]/2: “The condition shall be of integral type, enumeration
type, or of a class type for which a single non-explicit conversion
function to integral or enumeration type exists (12.3).”
and says:
The principal issue, in each of the four contexts cited in the
Introduction, seems to lie in their common helpful but very strict
requirement that limits a class to only one conversion operator [...]
Another concern is the scope of the qualifier “single” in the current
wording. Must there be but a single conversion function in the class,
or may there be several so long as a single one is appropriate to the
context?
The current language seems unclear on this point. It is also
unclear whether a conversion operator that produces a reference to an
appropriate type is an appropriate conversion operator. (A question on
this point was posted to the Core reflector on 2011-02-21, but has
gone unanswered as of this writing.) Current compiler practice seems
to admit such operators, but the current language seems not to.
and proposes:
To address all these concerns, we recommend instead to use the proven
approach typified by the term contextually converted to bool as
defined in [conv]/3. We therefore propose a modest addition to
[conv]/3 to define contextual conversion to other specified types, and
then appeal to this new definition.
and the new language would be as follows;
Certain other language constructs require similar conversion, but to a
value having one of a specified set of types appropriate to the
construct. An expression e of class type E appearing in such a context
is said to be contextually implicitly converted to a specified type T
and is well-formed if and only if e can be implicitly converted to a
type T that is determined as follows: E is searched for conversion
functions whose return type is cv T or reference to cv T such that T
is allowed by the context. There shall be exactly one such T.
Note N3486: C++ Editor's Report, October 2012 shows us when N3323 was incorporated in the draft standard.
Update
Filed a gcc bug report.
6.4.2/2 The switch statement (emphasis mine)
The condition shall be of integral type, enumeration type, or of a class type for which a single non-explicit conversion function to integral or enumeration type exists (12.3). If the condition is of class type, the condition is converted by calling that conversion function, and the result of the conversion is used in place of the original condition for the remainder of this section.
So my interpretation is that g++ is correct here.
I believe gcc is correct, but the standard is flawed.
gcc is correct because the standard mandates a single non-explicit conversion operator to integral or enumeration types for types used in switch.
The standard is wrong because detecting that case involves solving the halting problem.
operator T can have a SFINAE clause of arbitrary complexity attached to it. The compiler, under the standard, must determine if there is a T such that the T is an enum.
template<class...Ts>
struct evil {
enum { bob = 3+sizeof...(Ts) };
};
struct test {
operator int() const { return -1; };
template<class T, typename std::enable_if<T::bob==2>::type* unused=nullptr>
operator T() const { return T::bob; }
};
int main() {
switch( test{} ) {
case -1: std::cout << "int\n"; break;
case 2: std::cout << "bob\n"; break;
default: std::cout << "unexpected\n"; break;
}
}
The above code demonstrates a case where we have an infinite number of enums implicitly available. We have an operator T that will cast to type T if and only if T::bob==2. Now, there are no such enums in our program (and even if we removed the 3+ there would still not be, because it is not an enum class -- easily rectified).
So test can only be converted to int, and as such the switch statement should compile. gcc fails this test, and claims that the template operator T makes it ambiguous (without telling us what T, naturally).
Replacing enum type with enum class type, and removing the 3+ makes the switch statement illegal under the standard. But for the compiler to figure that out, it basically has to instantiate all possible templates in the program looking for a secret enum with the property in question. With a bit of work, I can thus force the compiler to solve NP complete problems (or, excluding compiler limitations, the halting problem) in order to determine if a progrma should compile or not.
I do not know what the right wording should be. But the wording as written sure isn't sound.
In my hummble opinion and based on §13.3.3/1 Best viable function [over.match.best], the non-template overloaded conversion operator (i.e., operator int() const) has a higher precedence in terms of overload resolution picking, than its template counterpart (i.e., template <typename T> operator T () const).
Thus, overloaded resolution would correctly choose operator int() const over template <typename T> operator T () const since is the best viable function.
Furthermore, and since the non-template version would be chosen over the template one (i.e., the template would not be materialized/qualified by the compiler), class Var would have a single conversion function and thus the requirement in §6.4.2/2 The switch statement [stmt.switch] for single integral conversion would be satisfied.
Consequently, Clang is right and GCC is wrong.
Here are the relevant quotes, but the final answer depends quite a bit on interpretation. I can't even decide on a favorite right now.
N3797 6.4.2/2:
The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (Clause 4) to an integral or enumeration type.
4/5:
Certain language constructs require conversion to a value having one of a specified set of types appropriate to the construct. An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.
14.5.2/6:
A specialization of a conversion function is not found by name lookup. Instead, any conversion function templates visible in the context of the use are considered. For each such operator, if argument deduction succeeds (14.8.2.3), the resulting specialization is used as if found by name lookup.
14.5.2/8:
Overload resolution (13.3.3.2) and partial ordering (14.5.6.2) are used to select the best conversion function among multiple specializations of conversion function templates and/or non-template conversion functions.
Interpretation 1: 4/5 says "conversion functions", not "conversion functions and conversion function templates". Therefore Var::operator int() const is the only option, and clang is correct.
Interpretation 2 [weak?]: 14.5.2 requires us to compare the conversion function template by overload resolution and partial ordering, on the same initial standing as the non-template conversion function. Those compare function template specializations and functions, not function templates, so we'll do template argument deduction. Template argument deduction for a conversion function template requires a target type. Although we usually have a clearer target type, in this case we'll just try (in theory anyway) all types in the set of allowable types. But it is clear that the non-template function is a better viable function that all the template specializations, so overload resolution selects the non-template function. clang is correct.
Interpretation 3: Since overload resolution requires template argument deduction, and template argument deduction requires a known target type, the semantics of 4/5 must be considered first, and then its converted type (if any) can be used for the overload resolution process. 14.5.2 requires that the conversion function template be considered, but then we find that there are multiple valid types T for which we have a conversion function to T [that function possibly being a function template specialization]. The program is ill-formed, and therefore g++ is correct.
If i'm reading this section correctly on overloading, Clang is correct
13.3.3 Best viable function [over.match.best]
[...] Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then [...]
— F1 is a non-template function and F2 is a function template specialization, or, if not that,[...]
The Draft is free to read. Not sure if any changes in 13.3.3 were put into the final spec (i haven't payed for it)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
I'd file a G++ bug :-) They might fire back with a different section of the standard to justify, but it appears to be non-standards compliant.
Edit for aschepler's comment:
From: http://publib.boulder.ibm.com/infocenter/comphelp/v101v121/index.jsp?topic=/com.ibm.xlcpp101.aix.doc/language_ref/cplr315.html
Suppose that f is an overloaded function name. When you call the overloaded function f(), the compiler creates a set of candidate functions. This set of functions includes all of the functions named f that can be accessed from the point where you called f(). The compiler may include as a candidate function an alternative representation of one of those accessible functions named f to facilitate overload resolution.
After creating a set of candidate functions, the compiler creates a set of viable functions. This set of functions is a subset of the candidate functions. The number of parameters of each viable function agrees with the number of arguments you used to call f().

How will C++17 exception specifier type system work?

Studying about "noexcept specifier(and operator)", I wrote a simple code. And I am surprised that this piece of code:
void asdf() noexcept {}
int main()
{
auto f = asdf;
std::cout << std::boolalpha << noexcept(f()) << std::endl;
}
prints false, even function "asdf" is noexcept-specified.
So while searching why this mysterious phenomenon is happening, I found C++17's "exception specifier type system"- P0012R1.
According to this (accepted) proposal, since C++17; as noexcept is part of function type, will the code above print true?
And one more, in this question's one line:
std::function<void() noexcept> f
The noexcept specifying seems ignored in C++14 or 11.
Will this code work as intended in C++17?
According to this (accepted) proposal, since C++17; as noexcept is part of function type, will the code above print true?
Yes.
The type of f will be deduced to void(*)() noexcept since the function-to-pointer conversion applied to asdf will preserve the noexcept property. A call to a noexcept function pointer certainly cannot throw an exception, unless one of its subexpressions does.
For the exact wording, see [expr.unary.noexcept]/3 and [expect.spec]/13. Note that the new wording in the latter paragraph from the C++17 draft comes from P0012R1, which is linked in the OP.
The result of the noexcept operator is true if the set of potential exceptions of the expression ([except.spec]) is empty, and false otherwise.
...
If e is a function call ([expr.call]):
If its postfix-expression is a (possibly parenthesized) id-expression ([expr.prim.id]), class member access ([expr.ref]), or pointer-to-member operation ([expr.mptr.oper]) whose cast-expression is an id-expression, S is the set of types in the exception specification of the entity selected by the contained id-expression (after overload resolution, if applicable).
...
So the set of potential exceptions of f() is the same as the set of types in the exception specification of f, which is empty since f is declared noexcept.
Let's move on to the second question:
The noexcept specifying seems ignored in C++14 or 11. Will this code work as intended in C++17?
Your question seems to be: will std::function<void() noexcept> refuse to hold a function that can throw exceptions?
I would say that it's unclear. In the present wording of the standard, std::function<void() noexcept> is not actually defined, just as std::function<double(float) const> is not defined. This was of course not an issue in C++14, since noexcept was not considered part of a function's type.
Will std::function<void() noexcept> simply break in C++17? That's uncertain to me. Let's take a look at the current wording to guess what the behaviour "should" be.
The standard requires the argument to the constructor of std::function<R(ArgTypes..)> to be "Lvalue-Callable" for argument types ArgTypes... and return type R, which means:
A callable type ([func.def]) F is Lvalue-Callable for argument types ArgTypes and return type R if the expression INVOKE(declval<F&>(), declval<ArgTypes>()..., R), considered as an unevaluated operand (Clause [expr]), is well formed ([func.require]).
Perhaps there should be an additional requirement that if the function type is noexcept, then noexcept(INVOKE(...)) must be true as well. Nonetheless, this wording is not present in the current draft.
In P0012R1, there is a comment that:
It is an open issue how to propagate "noexcept" through std::function.
My guess is that they mean that it is not clear how std::function could be implemented if this additional requirement were imposed. Hopefully, someone else can provide more details.

Classes with both template and non-template conversion operators in the condition of switch statement

The problem originally arose in this question. Consider the following code:
class Var
{
public:
operator int () const
{ return 0; }
template <typename T>
operator T () const
{ return T(); }
};
int main()
{
Var v;
switch (v)
{ }
}
Without the operator int() const { return 0; }, both g++ and clang reject the code.
However, the above code, with the operator int(), is accepted by clang but rejected by g++ with the following error:
main.cpp:17:14: error: default type conversion can't deduce template argument for 'template<class T> Var::operator T() const'
switch (v)
^
Which compiler is correct?
I believe clang is correct here.
We can see from the draft C++ standard section 6.4.2 The switch statement that this involves a contextually implicit conversion. Paragraph 2 says (*emphasis mine going forward):
The condition shall be of integral type, enumeration type, or class
type. If of class type, the condition is contextually implicitly
converted (Clause 4) to an integral or enumeration type.
We can see the section we need to use is 4 Standard conversions and paragraph 5 covers these cases, it says:
Certain language constructs require conversion to a value having one
of a specified set of types appropriate to the construct. An
expression e of class type E appearing in such a context is said to be
contextually implicitly converted to a specified type T and is
well-formed if and only if e can be implicitly converted to a type T
that is determined as follows: E is searched for conversion functions
whose return type is cv T or reference to cv T such that T is allowed
by the context. There shall be exactly one such T.
This does not reference section 8.5 which allows for overload resolution by specifically referring to section 13.3 without allowing overload resolution we can not use:
template <typename T>
operator T () const
and therefore there is no ambiguity.
Note this is different from paragraph 4 which covers bool conversions in contexts of if, while etc... and says (emphasis mine):
Certain language constructs require that an expression be converted to
a Boolean value. An expression e appearing in such a context is said
to be contextually converted to bool and is well-formed if and only if
the declaration bool t(e); is well-formed, for some invented temporary
variable t (8.5).
which specifically allows for overload resolution and refers directly to section 13.3 which covers this. It makes sense that it is allowed since we have a specific destination type bool to convert to which we don't have in the switch case.
Why
We can figure this out by going looking at N3323: A Proposal to Tweak Certain C++ Contextual Conversions, v3 it covers this issue. It would be hard to quote the whole paper so I will attempt to quote enough of the context. It says:
The context in which a C++ expression appears often influences how the
expression is evaluated, and therefore may impose requirements on the
expression to ensure such evaluation is possible. [...]
In four cases, the FDIS (N3290) uses different language to specify an
analogous contextdependent conversion. In those four contexts, when an
operand is of class type, that type must have a “single non-explicit
conversion function” to a suitable (context-specific) type. [...]
and includes:
[stmt.switch]/2: “The condition shall be of integral type, enumeration
type, or of a class type for which a single non-explicit conversion
function to integral or enumeration type exists (12.3).”
and says:
The principal issue, in each of the four contexts cited in the
Introduction, seems to lie in their common helpful but very strict
requirement that limits a class to only one conversion operator [...]
Another concern is the scope of the qualifier “single” in the current
wording. Must there be but a single conversion function in the class,
or may there be several so long as a single one is appropriate to the
context?
The current language seems unclear on this point. It is also
unclear whether a conversion operator that produces a reference to an
appropriate type is an appropriate conversion operator. (A question on
this point was posted to the Core reflector on 2011-02-21, but has
gone unanswered as of this writing.) Current compiler practice seems
to admit such operators, but the current language seems not to.
and proposes:
To address all these concerns, we recommend instead to use the proven
approach typified by the term contextually converted to bool as
defined in [conv]/3. We therefore propose a modest addition to
[conv]/3 to define contextual conversion to other specified types, and
then appeal to this new definition.
and the new language would be as follows;
Certain other language constructs require similar conversion, but to a
value having one of a specified set of types appropriate to the
construct. An expression e of class type E appearing in such a context
is said to be contextually implicitly converted to a specified type T
and is well-formed if and only if e can be implicitly converted to a
type T that is determined as follows: E is searched for conversion
functions whose return type is cv T or reference to cv T such that T
is allowed by the context. There shall be exactly one such T.
Note N3486: C++ Editor's Report, October 2012 shows us when N3323 was incorporated in the draft standard.
Update
Filed a gcc bug report.
6.4.2/2 The switch statement (emphasis mine)
The condition shall be of integral type, enumeration type, or of a class type for which a single non-explicit conversion function to integral or enumeration type exists (12.3). If the condition is of class type, the condition is converted by calling that conversion function, and the result of the conversion is used in place of the original condition for the remainder of this section.
So my interpretation is that g++ is correct here.
I believe gcc is correct, but the standard is flawed.
gcc is correct because the standard mandates a single non-explicit conversion operator to integral or enumeration types for types used in switch.
The standard is wrong because detecting that case involves solving the halting problem.
operator T can have a SFINAE clause of arbitrary complexity attached to it. The compiler, under the standard, must determine if there is a T such that the T is an enum.
template<class...Ts>
struct evil {
enum { bob = 3+sizeof...(Ts) };
};
struct test {
operator int() const { return -1; };
template<class T, typename std::enable_if<T::bob==2>::type* unused=nullptr>
operator T() const { return T::bob; }
};
int main() {
switch( test{} ) {
case -1: std::cout << "int\n"; break;
case 2: std::cout << "bob\n"; break;
default: std::cout << "unexpected\n"; break;
}
}
The above code demonstrates a case where we have an infinite number of enums implicitly available. We have an operator T that will cast to type T if and only if T::bob==2. Now, there are no such enums in our program (and even if we removed the 3+ there would still not be, because it is not an enum class -- easily rectified).
So test can only be converted to int, and as such the switch statement should compile. gcc fails this test, and claims that the template operator T makes it ambiguous (without telling us what T, naturally).
Replacing enum type with enum class type, and removing the 3+ makes the switch statement illegal under the standard. But for the compiler to figure that out, it basically has to instantiate all possible templates in the program looking for a secret enum with the property in question. With a bit of work, I can thus force the compiler to solve NP complete problems (or, excluding compiler limitations, the halting problem) in order to determine if a progrma should compile or not.
I do not know what the right wording should be. But the wording as written sure isn't sound.
In my hummble opinion and based on §13.3.3/1 Best viable function [over.match.best], the non-template overloaded conversion operator (i.e., operator int() const) has a higher precedence in terms of overload resolution picking, than its template counterpart (i.e., template <typename T> operator T () const).
Thus, overloaded resolution would correctly choose operator int() const over template <typename T> operator T () const since is the best viable function.
Furthermore, and since the non-template version would be chosen over the template one (i.e., the template would not be materialized/qualified by the compiler), class Var would have a single conversion function and thus the requirement in §6.4.2/2 The switch statement [stmt.switch] for single integral conversion would be satisfied.
Consequently, Clang is right and GCC is wrong.
Here are the relevant quotes, but the final answer depends quite a bit on interpretation. I can't even decide on a favorite right now.
N3797 6.4.2/2:
The condition shall be of integral type, enumeration type, or class type. If of class type, the condition is contextually implicitly converted (Clause 4) to an integral or enumeration type.
4/5:
Certain language constructs require conversion to a value having one of a specified set of types appropriate to the construct. An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.
14.5.2/6:
A specialization of a conversion function is not found by name lookup. Instead, any conversion function templates visible in the context of the use are considered. For each such operator, if argument deduction succeeds (14.8.2.3), the resulting specialization is used as if found by name lookup.
14.5.2/8:
Overload resolution (13.3.3.2) and partial ordering (14.5.6.2) are used to select the best conversion function among multiple specializations of conversion function templates and/or non-template conversion functions.
Interpretation 1: 4/5 says "conversion functions", not "conversion functions and conversion function templates". Therefore Var::operator int() const is the only option, and clang is correct.
Interpretation 2 [weak?]: 14.5.2 requires us to compare the conversion function template by overload resolution and partial ordering, on the same initial standing as the non-template conversion function. Those compare function template specializations and functions, not function templates, so we'll do template argument deduction. Template argument deduction for a conversion function template requires a target type. Although we usually have a clearer target type, in this case we'll just try (in theory anyway) all types in the set of allowable types. But it is clear that the non-template function is a better viable function that all the template specializations, so overload resolution selects the non-template function. clang is correct.
Interpretation 3: Since overload resolution requires template argument deduction, and template argument deduction requires a known target type, the semantics of 4/5 must be considered first, and then its converted type (if any) can be used for the overload resolution process. 14.5.2 requires that the conversion function template be considered, but then we find that there are multiple valid types T for which we have a conversion function to T [that function possibly being a function template specialization]. The program is ill-formed, and therefore g++ is correct.
If i'm reading this section correctly on overloading, Clang is correct
13.3.3 Best viable function [over.match.best]
[...] Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then [...]
— F1 is a non-template function and F2 is a function template specialization, or, if not that,[...]
The Draft is free to read. Not sure if any changes in 13.3.3 were put into the final spec (i haven't payed for it)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
I'd file a G++ bug :-) They might fire back with a different section of the standard to justify, but it appears to be non-standards compliant.
Edit for aschepler's comment:
From: http://publib.boulder.ibm.com/infocenter/comphelp/v101v121/index.jsp?topic=/com.ibm.xlcpp101.aix.doc/language_ref/cplr315.html
Suppose that f is an overloaded function name. When you call the overloaded function f(), the compiler creates a set of candidate functions. This set of functions includes all of the functions named f that can be accessed from the point where you called f(). The compiler may include as a candidate function an alternative representation of one of those accessible functions named f to facilitate overload resolution.
After creating a set of candidate functions, the compiler creates a set of viable functions. This set of functions is a subset of the candidate functions. The number of parameters of each viable function agrees with the number of arguments you used to call f().