Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should deduction guide argument initialization considered by class template specialization deduction?

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?

like image 817
Oliv Avatar asked Jul 09 '18 11:07

Oliv


1 Answers

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.

like image 167
Barry Avatar answered Nov 20 '22 00:11

Barry