Gotchas with template argument subtraction for class templates

I read an article about template argument deduction for class templates here http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html . This feature is in the C ++ 17 standard and some things confused me.

template <typename T>
class Something {
public:

    // delete the copy and move constructors for simplicity
    Something(const Something&) = delete;
    Something(Something&&) = delete;

    explicit Something(T&&) { ... }
    explicit Something(const T&) { ... }

    template <typename U, typename EnableIfNotT<U, T>* = nullptr>
    Something(U&&) { ... }
};

      

Given the above code, if anyone is trying to instantiate the above template like this

auto something = Something{std::shared_ptr<int>{}};

      

will the reference rvalue overload always be called? Since the set of overloads considered for inference is

template <typename T>
Something<T> F(T&&) { ... }
template <typename T>
Something<T> F(const T&) { ... }
template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&) { ... }

      

  • The second overload will never be preferred over the first (so there is now a link overload overload, instead of being an Rvalue overload), so what should happen here?
  • And it looks like the latter can never be called without explicitly specifying a parameter T

    , is this the intended behavior?
  • Also are there any other getter or style rules to keep in mind when using template argument subtraction for class templates?
  • Next, do user-defined subtraction instructions follow after class definition? For example, can you have a return return type in a class constructor declaration in the class definition itself? (As opposed to the iterator constructor here http://en.cppreference.com/w/cpp/language/class_template_deduction )
+3


source to share


1 answer


  • The second overload will never be preferable to the first ( so now a link forwarding overload instead of being an Rvalue overload), so what should happen here?

No, this is not a forwarding link. This is a key difference. From [temp.deduct.call] :

The forwarder reference is an rvalue reference to the cv-unqualified template parameter, which does not represent the class template template (at the time of the template template argument ([over.match.class.deduct])).

Your candidates:

template <typename T>
Something<T> F(T&&);       // this ONLY matches non-const rvalues

template <typename T>
Something<T> F(const T&);  // this matches everything

template <typename T, typename U, typename EnableIfNotT<U, T>*>
Something<T> F(U&&);       // this matches nothing

      

When you write:

auto something = Something{std::shared_ptr<int>{}};

      



Constructor T&&

is preferred, s T=std::shared_ptr<int>

, so you get Something<std::shared_ptr<int>>

as your specialization over template. If instead it was written:

std::shared_ptr<int> p;
auto something = Something{p};

      

then the constructor is preferred T const&

(indeed, this is the only viable candidate). While we were in the same location Something<std::shared_ptr<int>>

.

  1. And it looks like the latter can never be called without explicitly specifying the T parameter, is this the intended behavior?

That's right T

- not deducible context. It makes sense - this constructor exists to perform conversions, but you need to specify what you are converting to in order to do the conversion. It would be pointless to have this "fair job" for you.

  1. Next, do user-defined subtraction instructions follow after class definition?

Yes. This is where they go, according to the rules. There is no point in returning a return type in a constructor - the constructor does not return anything.

+1


source







All Articles