10

As a follow up of this question, I tested the behavior of both clang and gcc. It appears that the two compiler have a different interpretation of the c++ standard.

In the example below, GCC refuses to compile, if a non copyable argument would need to be copied according to the deduction guide hypothetical constructor argument. Clang does not perform this check:

#include <cstddef>

struct not_copyable{
    not_copyable()=default;
    not_copyable(const not_copyable&)=delete;
};
struct movable{
    movable()=default;
    movable(movable&&);
};

template <typename T, size_t N>
struct A
 { template <typename ... Ts> A (Ts const & ...) {} };

template <typename T, size_t N>
struct B
 { template <typename ... Ts> B (const Ts & ...) {} };

template <typename T, typename ... Ts>
A(T const &, Ts const & ...) -> A<T, 1U + sizeof...(Ts)>;

template <typename T, typename ... Ts>
B(T, Ts ...) -> B<T, 1 + sizeof...(Ts)>;


int main()
 {
   not_copyable nc;
   movable m;

   auto a0 = A{nc};    // gcc & clang -> compile
   auto a1 = A{m};     // gcc & clang -> compile
   auto b0 = B{nc};    // clang ->compile;  gcc -> error
   auto b1 = B{m};     // clang ->compile;  gcc -> error
 }

In think the right behavior is defined in this paragraph of the C++ standard [over.match.class.deduct]/2:

Initialization and overload resolution are performed as described in [dcl.init] and [over.match.ctor], [over.match.copy], or [over.match.list] (as appropriate for the type of initialization performed) for an object of a hypothetical class type, where the selected functions and function templates are considered to be the constructors of that class type for the purpose of forming an overload set,[...]

I emphasized "for the purpose of forming an overload set" because I think this is where clang and gcc diverge. Clang does not seem to check if the deduction guide hypothetical constructor is a viable function, but gcc does. Which compiler is right?

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • It is indeed a viable function as [over.best.ics]/2 says. I think you are asking whether the viability of the implicit conversion should be considered. – xskxzr Jul 09 '18 at 11:46
  • I think GCC is right because [over.match.class.deduct]/2 also says "All such notional constructors are considered to be public members of the hypothetical class type". If the viability of the whole initialization is not considered, this sentence is unnecessary. Well, this is only my guess. – xskxzr Jul 09 '18 at 11:54
  • @xskxzr So the implicit conversion sequence exists because it is declared so it is viable, this is what means the standard? If I follow you the fact it is defined as deleted should not be taken into acount by gcc no? – Oliv Jul 09 '18 at 12:02

1 Answers1

6

Clang does not seem to check if the deduction guide hypothetical constructor is a viable function, but gcc does.

Actually, the deduction guide is a viable function. A function being viable just means that the number of arguments matches, the constraints are satisfied, and you can form implicit conversion sequences for each parameter/argument pair. And when we're checking if an ICS exists, [over.best.ics]/2:

Other properties, such as the lifetime, storage class, alignment, accessibility of the argument, whether the argument is a bit-field, and whether a function is deleted, are ignored.

It's very important that deleting a function does not make it non-viable, because it's important that it can still end up being the best viable candidate. This means that the fact that not_copyable's copy constructor is deleted should only come into effect when we're actually invoking it.

For example, both gcc and clang reject this program. #1 is a viable candidate, and it's the best viable candidate, despite the deleted copy constructor:

struct NC {
    NC() = default;
    NC(NC const&) = delete;
    NC& operator=(NC const&) = delete;
};       

void foo(NC );                            // #1
template <typename T> void foo(T const&); // #2

int main() {
    NC nc;
    foo(nc);
}

But we're never actually invoking the synthesized functions and function templates that we use for deduction. We're just performing overload resolution and selecting the best candidate - which we're only using to pick the class type, and then we start over. At no point should we actually require copying.

I think this is a gcc bug. Filed 86439.

xskxzr
  • 12,442
  • 12
  • 37
  • 77
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    I don't know. On the one hand this is perfectly reasonable. But on the other hand, not actually calling the function, and just using it to select a type is still ill-formed [according to clang in other contexts](http://coliru.stacked-crooked.com/a/88a71fbb38aa10c7) – StoryTeller - Unslander Monica Jul 09 '18 at 12:08
  • @StoryTeller Probably an other compiler bug because decltype's operand is an unevaluated operand and so its [definition does not need to be provided](http://eel.is/c++draft/expr.prop#expr.context-1), no? – Oliv Jul 09 '18 at 12:15
  • 2
    @Oliv - The behavior with `decltype` is correct (since C++11) according to [\[dcl.fct.def.delete\]/2](https://timsong-cpp.github.io/cppwp/n4659/dcl.fct.def.delete#2). I guess this is why GCC emits and error here too. Like I said, I don't know. But I think Barry's argument is a good one for why it *shouldn't* be an error. – StoryTeller - Unslander Monica Jul 09 '18 at 12:18
  • @StoryTeller This paragraph you site seems indeed to both justify Barry's answer and the fact that clang rejects the expression inside decltype's operand. What a complexity! – Oliv Jul 09 '18 at 12:23
  • @StoryTeller Good example... I... don't know. – Barry Jul 09 '18 at 12:25
  • @StoryTeller It's ill-formed there because it goes through [expr.call]p7. There are no other contexts I think that involve just overload resolution other than CTAD. – Rakete1111 Jul 09 '18 at 18:18
  • @Rakete1111 - All I see at p7 is [note](https://timsong-cpp.github.io/cppwp/n4659/expr.call#7). Are you looking at another draft? – StoryTeller - Unslander Monica Jul 09 '18 at 18:39
  • @Rakete1111 Wouldn't an unevaluated context also be such a context? – Passer By Jul 09 '18 at 20:14
  • @StoryTeller Oops, yes I am. It's p4 in your draft, the paragraph about initializing arguments. – Rakete1111 Jul 09 '18 at 20:14
  • @PasserBy No I don't think so. But honestly, I have no idea where in the standard that is specified. – Rakete1111 Jul 09 '18 at 20:21