3

Considering type A:

template <typename T, size_t N>
struct A
{
    T arr[N];
};

Is there any difference between C++17 user-defined deduction guides

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

and

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

?

Or, in other words is there any difference between const references and values in deduction guides?


Please note that the question is not about template function type deduction, but about the new C++17 feature, user-defined deduction guides for class template argument deduction, so you can simply declare A instance{1,2,3} instead of A<int, 3> instance{1,2,3}.

max66
  • 65,235
  • 10
  • 71
  • 111
plasmacel
  • 8,183
  • 7
  • 53
  • 101

2 Answers2

3

Or, in other words is there any difference between const references and values in deduction guides?

In your case maybe not but, generally speaking, yes.

When T isn't copyable.

In the following example the first case (const reference) compile receiving a std::unique_ptr<int>, the second one (value) gives an error

#include <iostream>
#include <memory>

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 (Ts const & ...) {} };

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()
 {
   std::unique_ptr<int> up;

   auto a = A{up};    // compile
   // auto b = B{up}; // doesn't compile
 }
plasmacel
  • 8,183
  • 7
  • 53
  • 101
max66
  • 65,235
  • 10
  • 71
  • 111
  • interesting. Haven't thought of that. Since the constructor is ok (taking references) I would thing there is no problem. Good catch. – bolov Jul 08 '18 at 20:54
  • @bolov - works also is the `B` constructor take copies; but doesn't works for the `A` in the question (because the initialization of the array `arr` require copies) – max66 Jul 08 '18 at 20:58
  • 1
    `auto b = B{up};` compiles [under Clang](https://godbolt.org/g/2bUcrV). – xskxzr Jul 09 '18 at 08:44
  • 1
    [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." – xskxzr Jul 09 '18 at 08:56
  • 1
    @xskxzr - I'm not a standard guru but I don't think the fragment you cite say that `auto b = B{up};` is OK; follows (i'm reading C++17 standard, so 16.3.3.1.2) "So, although an implicit conversion sequence can be defined for a given argument-parameter pair, the conversion from the argument to the parameter might still be ill-formed in the final analysis". And I suppose this is the case: a deleted function is selected (the copy constructor for `std::unique_ptr` so the code is "ill-formed in the final analysis". But I repeat: I'm not an expert so I'll prepare a different question for C++ gurus. – max66 Jul 09 '18 at 11:47
  • @xskxzr - but you're right regarding clang++: compile; thanks. – max66 Jul 09 '18 at 11:48
  • According to [this answer](https://stackoverflow.com/a/51245110/5632316) `B{up}` should compile – Oliv Jul 09 '18 at 12:17
  • @Oliv - First of all, thanks for preparing the question: I was trying to formulate one but was confusing and your is really better. Second: Barry is a guru standard but also StoryTeller is far more expert than me so... I wait and I see what they can conclude. – max66 Jul 09 '18 at 16:09
  • @max66 If Barry don't know..., I'll wait a little and then ask on a slower channel. – Oliv Jul 09 '18 at 17:45
1

A difference is that when using values in deduction guides, the arguments will decay for template argument deduction. For example,

#include <cstddef>
using std::size_t;

template <typename T, size_t N>
struct A
{
    T arr[N];
};

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

template <typename T, size_t N>
struct B
{
    T arr[N];
};

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

int main()
{
    int test[42];
    A instanceA {test}; // deduced to be A<int[42], 1>, error 
    B instanceB {test}; // deduced to be B<int*, 1>, ok but the effect may be unexpected
}
xskxzr
  • 12,442
  • 12
  • 37
  • 77