9

Here's a short example that reproduces this “no viable conversion” with lemon for clang but valid for g++ difference in compiler behavior.

#include <iostream>

struct A { 
    int i; 
};

#ifndef UNSCREW_CLANG
using cast_type = const A;
#else 
using cast_type = A;
#endif

struct B {
    operator cast_type () const {
        return A{i};
    }
    int i;
}; 

int main () { 
    A a{0};
    B b{1};

#ifndef CLANG_WORKAROUND
    a = b;
#else    
    a = b.operator cast_type ();
#endif    

    std::cout << a.i << std::endl;    

    return EXIT_SUCCESS;
}

live at godbolt's

g++ (4.9, 5.2) compiles that silently; whereas clang++ (3.5, 3.7) compiles it

if

using cast_type = A;

or

using cast_type = const A;
// [...] 
a = b.operator cast_type ();

are used, but not with the defaulted

using cast_type = const A;
// [...] 
a = b; 

In that case clang++ (3.5) blames a = b:

testling.c++:25:9: error: no viable conversion from 'B' to 'A'
    a = b;
        ^
testling.c++:3:8: note: candidate constructor (the implicit copy constructor) 
not viable:
      no known conversion from 'B' to 'const A &' for 1st argument
struct A { 
       ^
testling.c++:3:8: note: candidate constructor (the implicit move constructor) 
not viable:
      no known conversion from 'B' to 'A &&' for 1st argument
struct A { 
       ^
testling.c++:14:5: note: candidate function
    operator cast_type () const {
    ^
testling.c++:3:8: note: passing argument to parameter here
struct A { 

With reference to the 2011¹ standard: Is clang++ right about rejecting the defaulted code or is g++ right about accepting it?

Nota bene: This is not a question about whether that const qualifier on the cast_type makes sense. This is about which compiler works standard-compliant and only about that.

¹ 2014 should not make a difference here.

EDIT:

Please refrain from re-tagging this with the generic c++ tag. I'd first like to know which behavior complies to the 2011 standard, and keep the committees' dedication not to break existing (< 2011) code out of ansatz for now.

Community
  • 1
  • 1
decltype_auto
  • 1,706
  • 10
  • 19
  • 1
    I tried to reproduce too : http://goo.gl/dQ7EFa – Isammoc Nov 12 '15 at 14:10
  • 1
    With your before c++11 compatible code : http://goo.gl/HHeIvg , if you remove compiler options (`-std=c++11`), clang 3.7 succeeds... – Isammoc Nov 12 '15 at 14:25
  • @Isammoc: Yup, but g++ fails with each and every right, because the `using` directives are a 2011 extension. clang++, btw blames it as such, but just with a warning. Anyway I'd like that to be analyzed on the grounds of the 2011 standard (2014 should not make a difference here). – decltype_auto Nov 12 '15 at 14:33
  • @decltype_auto Why do you think 2014 shouldn't make a different here? (is it just a thought, or do you have some reason?) – Yakk - Adam Nevraumont Nov 12 '15 at 15:00
  • @Yakk: Because if there was a difference, regression tests would almost certainly have barked. – decltype_auto Nov 12 '15 at 15:12
  • @ShafikYaghmour. I dislike that tag edit; I chose the tags on purpose. – decltype_auto Nov 12 '15 at 15:17
  • @decltype_auto I understand, all the discussions I have read on meta say include the generic tag like [this one](http://meta.stackoverflow.com/q/265844/1708801). Although I have seen this a lot more often maybe we need a meta discussion for this, let me see if I can find a C++ version of this question on meta. – Shafik Yaghmour Nov 12 '15 at 15:21
  • @ShafikYaghmour: is that a standing rule on SO? You own proposed answer is a factual rationale for why not tagging it general c++. – decltype_auto Nov 12 '15 at 15:23
  • @decltype_auto I would not call it a rule, it is a consensus of the community. See this [question specific to C++](http://meta.stackoverflow.com/q/274921/1708801). You can leave out the generic C++ tag but it is almost for sure a high rep user will add it. – Shafik Yaghmour Nov 12 '15 at 16:07
  • @ShafikYaghmour: "I would not call it a rule" . Thus it is no rule. So what was wrong about my question's tagging **according the rules of SO** that demanded you, by your edit, marking my question as wrongfully tagged , please? – decltype_auto Nov 12 '15 at 16:16
  • @decltype_auto I made the edit in good faith based on the communities norms, in general that is how the community works. If you feel strongly you should revert the change. Keep in mind on future question you ask, this will likely happen again since this is the norm and most high rep users will add the C++ tag to questions that only have version specific tags in it. – Shafik Yaghmour Nov 12 '15 at 16:30
  • @ShafikYaghmour: "high rep" does not constitute a rationale for applying a different ruleset. – decltype_auto Nov 12 '15 at 16:41

1 Answers1

9

So it looks like this is covered by this clang bug report rvalue overload hides the const lvalue one? which has the following example:

struct A{};
struct B{operator const A()const;};
void f(A const&);
#ifdef ERR
void f(A&&);
#endif
int main(){
  B a;
  f(a);
}

which fails with the same error as the OP's code. Richard Smith toward the end says:

Update: we're correct to choose 'f(A&&)', but we're wrong to reject the initialization of the parameter. Further reduced:

  struct A {};
  struct B { operator const A(); } b;
  A &&a = b;

Here, [dcl.init.ref]p5 bullet 2 bullet 1 bullet 2 does not apply, because [over.match.ref]p1 finds no candidate conversion functions, because "A" is not reference-compatible with "const A". So we fall into [dcl.init.ref]p5 bullet 2 bullet 2, and copy-initialize a temporary of type A from 'b', and bind the reference to that. I'm not sure where in that process we go wrong.

but then comes back with another comment due to a defect report 1604:

DR1604 changed the rules so that

 A &&a = b;

is now ill-formed. So we're now correct to reject the initialization. But this is still a terrible answer; I've prodded CWG again. We should probably discard f(A&&) during overload resolution.

So it seems like clang is technically doing the right thing based on the standard language today but it may change since there seems to be disagreement at least from the clang team that this is the correct outcome. So presumably this will result in a defect report and we will have to wait till it is resolved before we can come to a final conclusion.

Update

Looks like defect report 2077 was filed based on this issue.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Good work, but does a statement like "we should **probably** discard [sth]" really make sense in a discussion about standard-defined behavior? It's "either-or" not "probably" and the truth values of those 'either-or' alternatives should be given by the standard, or not? – decltype_auto Nov 12 '15 at 15:34
  • @decltype_auto well Richard Smith is on the committee and so he believes if I understand correctly there is defect and the current resolution should be fixed. That is of course an opinion but an authoritative one and if the rest of the committee agrees with him then it will become a fact :-) – Shafik Yaghmour Nov 12 '15 at 16:13
  • But argumenta ad verecundiam aside, either the standard is not unambiguous in that regard, or clang's behavior either right or wrong in that regard, or do you disagree? – decltype_auto Nov 12 '15 at 16:36
  • @decltype_auto it is not always that clean cut we can see fro this [example here](http://stackoverflow.com/a/29683948/1708801) gcc is not conformant but believes the defect will resolve in their favor and so leaves a change in place. We can find compromises like this all the time and they often fall under quality of implementation. – Shafik Yaghmour Nov 12 '15 at 16:54
  • I'm not yet convinced that this should constitute the conclusion we draw here, but if you add that to your answer I'll accept it. And least we know, thanks to your good work, that it is an known issue and thus doesn't require filing a bug report ourself. Thank you! – decltype_auto Nov 12 '15 at 17:01