Overloaded operator preference [duplicate] - c++

struct A{
operator auto(){
return 0;
}
};
int main(){
A a;
a.operator auto(); // #1
a.operator int(); // #2
}
GCC accepts that #2 is the right way to call the conversion function explicitly while Clang accepts #1.
It seems that #1 is ill-formed due to the following rule:
dcl.spec.auto#6
A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
This usage a.operator auto() is not explicitly allowed in section [dcl.spec.auto], hence it should be ill-formed. However, for the second usage, which is accepted by GCC, the standard does not say that the conversion-function-id where the conversion-type-id is replaced by the deduced type denotes the name of the conversion function. In other words, the declared conversion-function-id in the declaration is operator auto rather than operator int. The former has the same token as the declarator-id of the declaration. According to the grammar, the unqualified-id operator auto should be the name of that conversion function. So, how to explicitly call this conversion function? Is it underspecified in the standard about which is the name of the conversion function when it contains a placeholder specifier?

It seems, that this is not specified precisely enough.
From 10.1.7.4 The auto specifier:
The placeholder type can appear with a function declarator in the
decl-specifier-seq, type-specifier-seq, conversion-function-id, or
trailing-return-type, in any context where such a declarator is valid.
Reading precisely, one might distinguish here between "can" and the stronger "can only", i.e. potentially opening up room for degrees of freedom for compiler intrinsics (strictly wrong vs. unspecified behavior).
And 3.4.5 class member access says:
7 If the id-expression is a conversion-function-id, its
conversion-type-id is first looked up in the class of the object
expression and the name, if found, is used.
Again leaving room for interpretation if the auto keyword can effectively be a fully qualified conversion-type-id within this context or not.
Your question itself might have to be further branched, namely
What are the overloading rules for the operator auto() usage in detail, i.e. should it be available for regular candidates competition already on class definition level? (not the case for Clang and Gcc, both accept the operator a priori besides an extra operator int() ...)
Can the operator auto() be called with explicit member operator referring (your case 1), i.e. effectively, has it a (unique) accessible name? Allowing that would be contradictory to all other explicitly allowed use cases for the keyword.
I've seen explicit tests for this within several clang revisions so its behavior is not an artefact of implicit naming convention applicance but an explicitly desired behavior obviously.
As already mentioned within the comments, Clang's behavior is a bit more overall consistent here at least in comparison to gcc since it's totally clear there, where the auto keyword is used for type deduction and where for name / function-id resolution. The operator auto() there is handled as a more explicit own entity, whereas for gcc, it has anonymous character similar to a lambda but is involved within candidates competition even for the explicit member operator access way.

Related

What is correct syntax for explicit call to conversion operator with deduced return type (auto) [duplicate]

struct A{
operator auto(){
return 0;
}
};
int main(){
A a;
a.operator auto(); // #1
a.operator int(); // #2
}
GCC accepts that #2 is the right way to call the conversion function explicitly while Clang accepts #1.
It seems that #1 is ill-formed due to the following rule:
dcl.spec.auto#6
A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
This usage a.operator auto() is not explicitly allowed in section [dcl.spec.auto], hence it should be ill-formed. However, for the second usage, which is accepted by GCC, the standard does not say that the conversion-function-id where the conversion-type-id is replaced by the deduced type denotes the name of the conversion function. In other words, the declared conversion-function-id in the declaration is operator auto rather than operator int. The former has the same token as the declarator-id of the declaration. According to the grammar, the unqualified-id operator auto should be the name of that conversion function. So, how to explicitly call this conversion function? Is it underspecified in the standard about which is the name of the conversion function when it contains a placeholder specifier?
It seems, that this is not specified precisely enough.
From 10.1.7.4 The auto specifier:
The placeholder type can appear with a function declarator in the
decl-specifier-seq, type-specifier-seq, conversion-function-id, or
trailing-return-type, in any context where such a declarator is valid.
Reading precisely, one might distinguish here between "can" and the stronger "can only", i.e. potentially opening up room for degrees of freedom for compiler intrinsics (strictly wrong vs. unspecified behavior).
And 3.4.5 class member access says:
7 If the id-expression is a conversion-function-id, its
conversion-type-id is first looked up in the class of the object
expression and the name, if found, is used.
Again leaving room for interpretation if the auto keyword can effectively be a fully qualified conversion-type-id within this context or not.
Your question itself might have to be further branched, namely
What are the overloading rules for the operator auto() usage in detail, i.e. should it be available for regular candidates competition already on class definition level? (not the case for Clang and Gcc, both accept the operator a priori besides an extra operator int() ...)
Can the operator auto() be called with explicit member operator referring (your case 1), i.e. effectively, has it a (unique) accessible name? Allowing that would be contradictory to all other explicitly allowed use cases for the keyword.
I've seen explicit tests for this within several clang revisions so its behavior is not an artefact of implicit naming convention applicance but an explicitly desired behavior obviously.
As already mentioned within the comments, Clang's behavior is a bit more overall consistent here at least in comparison to gcc since it's totally clear there, where the auto keyword is used for type deduction and where for name / function-id resolution. The operator auto() there is handled as a more explicit own entity, whereas for gcc, it has anonymous character similar to a lambda but is involved within candidates competition even for the explicit member operator access way.

How to explicitly call a conversion function whose conversion-type-id contains a placeholder specifier

struct A{
operator auto(){
return 0;
}
};
int main(){
A a;
a.operator auto(); // #1
a.operator int(); // #2
}
GCC accepts that #2 is the right way to call the conversion function explicitly while Clang accepts #1.
It seems that #1 is ill-formed due to the following rule:
dcl.spec.auto#6
A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
This usage a.operator auto() is not explicitly allowed in section [dcl.spec.auto], hence it should be ill-formed. However, for the second usage, which is accepted by GCC, the standard does not say that the conversion-function-id where the conversion-type-id is replaced by the deduced type denotes the name of the conversion function. In other words, the declared conversion-function-id in the declaration is operator auto rather than operator int. The former has the same token as the declarator-id of the declaration. According to the grammar, the unqualified-id operator auto should be the name of that conversion function. So, how to explicitly call this conversion function? Is it underspecified in the standard about which is the name of the conversion function when it contains a placeholder specifier?
It seems, that this is not specified precisely enough.
From 10.1.7.4 The auto specifier:
The placeholder type can appear with a function declarator in the
decl-specifier-seq, type-specifier-seq, conversion-function-id, or
trailing-return-type, in any context where such a declarator is valid.
Reading precisely, one might distinguish here between "can" and the stronger "can only", i.e. potentially opening up room for degrees of freedom for compiler intrinsics (strictly wrong vs. unspecified behavior).
And 3.4.5 class member access says:
7 If the id-expression is a conversion-function-id, its
conversion-type-id is first looked up in the class of the object
expression and the name, if found, is used.
Again leaving room for interpretation if the auto keyword can effectively be a fully qualified conversion-type-id within this context or not.
Your question itself might have to be further branched, namely
What are the overloading rules for the operator auto() usage in detail, i.e. should it be available for regular candidates competition already on class definition level? (not the case for Clang and Gcc, both accept the operator a priori besides an extra operator int() ...)
Can the operator auto() be called with explicit member operator referring (your case 1), i.e. effectively, has it a (unique) accessible name? Allowing that would be contradictory to all other explicitly allowed use cases for the keyword.
I've seen explicit tests for this within several clang revisions so its behavior is not an artefact of implicit naming convention applicance but an explicitly desired behavior obviously.
As already mentioned within the comments, Clang's behavior is a bit more overall consistent here at least in comparison to gcc since it's totally clear there, where the auto keyword is used for type deduction and where for name / function-id resolution. The operator auto() there is handled as a more explicit own entity, whereas for gcc, it has anonymous character similar to a lambda but is involved within candidates competition even for the explicit member operator access way.

Is a pointer to function (sometimes/always?) a function declarator?

(This question has been broken out from the discussion to this answer, which highlights CWG 1892)
Some paragraphs of the standard applies specific rules to function declarators; e.g. [dcl.spec.auto]/3 regarding placeholder types [emphasis mine]:
The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq, conversion-function-id, or trailing-return-type, in any context where such a declarator is valid. If the function declarator includes a trailing-return-type ([dcl.fct]), that trailing-return-type specifies the declared return type of the function. Otherwise, the function declarator shall declare a function. [...]
restricts where placeholder types may appear with(in) a function declarator. We may study the following example:
int f() { return 0; }
auto (*g)() = f; // #1
which both GCC and Clang accepts, deducing g to int(*)().
Is a pointer to function (sometimes/always?) a function declarator?
Or, alternatively, applied to the example, should #1 be rejected as per [dcl.spec.auto]/3, or does the latter not apply here as a pointer to function is not a function declarator (instead allowing #1 as per [dcl.spec.auto]/4 regarding variable type deduction from initializer)?
The rules for what is a given declarator is not entirely easy to follow, but we may note that, from [dcl.decl]/1
A declarator declares a single variable, function, or type, within a declaration.
that a given declarator is either any of a variable declarator, a function declarator or a type declarator.
[dcl.ptr] covers (variable) declarators that are pointers, but does not explicitly (/normatively) mention pointers to functions, albeit does so non-normatively in [dcl.ptr]/4
[dcl.fct] covers function declarators but does not mention function pointers as part of function declarations, other than a note that function types are checked during assignment/initialization to function pointers (which is not relevant for what a function declarator is)
My interpretation is that #1 is legal (as per the current standard), as it falls under a variable declarator. If this is actually correct, then the extended question (from the linked thread) is whether
template<auto (*g)()>
int f() { return g(); }
is legal or not (/intended to be legal or not as per CWG 1892); as the template parameter arguably contains a declarator that is a function pointer declarator, and not a function declarator.
We may finally note, as similarly pointed out in the linked to answer, that
template<auto g()> // #2
int f() { return g(); }
is arguably ill-formed (although this example is also accepted by both GCC and Clang), as the non-type template parameter at #2 is a function declarator and is thus used in an illegal context as per [dcl.spec.auto]/3, as it does not contain a trailing return type and does not declare a function.
The confusion here arises from two different meanings of "declarator": one is the portion of a declaration (after the specifiers) that pertains to one entity (or typedef-name), while the other is any of the several syntactic constructs used to form the former kind. The latter meaning gives rise to the grammar productions ptr-declarator (which also covers references) and noptr-declarator (which includes functions and arrays). That meaning is also necessary to give any meaning to a restriction that a "function declarator shall declare a function". Moreover, if we took the variable declaration
auto (*g)() = /*…*/;
to not involve a "function declarator" for the purposes of [dcl.spec.auto.general]/3, we would not be able to write
auto (*g)() -> int;
which is universally accepted (just as is the similar example in the question).
Moreover, while the statement that checks whether "the function declarator includes a trailing-return-type" inevitably refers to an overall declarator (which is what supports a trailing-return-type), it does so in its capacity as a "declaration operator" because it still allows the above cases with nested use of such operators. (What that limitation forbids is just
auto *f() -> int*;
where deduction would work but isn't performed at all here because it would always be useless.)
Meanwhile, there is some evidence, beyond implementation consensus, that the answer to the higher-level question is that auto in these cases should be allowed: [dcl.spec.auto.general]/1 says that auto in a function parameter serves to declare a generic lambda or abbreviated function template "if it is not the auto type-specifier introducing a trailing-return-type" rather than if it is not used with a function declarator at all.

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().

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().