Initialize class' template (aggregate type) member with aggregate initializer but without extra parenthesis
up vote
4
down vote
favorite
Having this code:
struct Vec3
int x;
int y;
int z;
;
template <typename T>
class myProperty
public:
myProperty(const T& initValue) : m_valueinitValue
private:
T m_value;
;
When creating myProperty
type object:
myProperty<int> ip1;
myProperty<Vec3> vp11, 2, 3;
// myProperty<Vec3> vp21, 2, 3; ERROR: myProperty doesn't have a matching constructor.
Is there an elegant way of making vp2
initialization work? Specializing myProperty
for Vec3
is an overkill.
c++ class templates constructor c++14
add a comment |
up vote
4
down vote
favorite
Having this code:
struct Vec3
int x;
int y;
int z;
;
template <typename T>
class myProperty
public:
myProperty(const T& initValue) : m_valueinitValue
private:
T m_value;
;
When creating myProperty
type object:
myProperty<int> ip1;
myProperty<Vec3> vp11, 2, 3;
// myProperty<Vec3> vp21, 2, 3; ERROR: myProperty doesn't have a matching constructor.
Is there an elegant way of making vp2
initialization work? Specializing myProperty
for Vec3
is an overkill.
c++ class templates constructor c++14
1
Is makingmyProperty
itself an aggregate an option? (I.e. remove the constructor and makem_value
public.) Otherwise the only solution is see is a templated constructor, that forwards its arguments to theT
constructor:template <typename ...P> myProperty(P &&... p) : m_value(std::forward<P>(p)...)
.
– HolyBlackCat
Nov 10 at 11:01
@HolyBlackCat, that won't compile. To make it work, you'll have to put extra parentheses, likem_value(std::forward<P>(p)...)
, that wayvp2
case will compile butvp1
not. And makingmyProperty
an agregate is not an option.
– nVxx
Nov 10 at 11:35
Oops. It should be: m_valuestd::forward<P>(p)...
, otherwisemyProperty<int> ip1;
doesn't compile. "vp2 case will compile but vp1 not" Does it mean you want bothvp1
andvp2
to compile, not onlyvp2
?
– HolyBlackCat
Nov 10 at 11:38
@HolyBlackCat, actually no, I don't want vp1 to compile :) My bad, didn't test your suggestion thoroughly, assumed something likevp4myVec3Objet
won't compile also, but it does, so this looks good :) Would accept the answer if you post it.
– nVxx
Nov 10 at 11:52
1
Turns out it breaks copy-construction (because it's a better match thanmyProperty(const myProperty &)
if the parameter is non-const). I'll post an answer if I figure out how to fix that in a neat way.
– HolyBlackCat
Nov 10 at 12:07
add a comment |
up vote
4
down vote
favorite
up vote
4
down vote
favorite
Having this code:
struct Vec3
int x;
int y;
int z;
;
template <typename T>
class myProperty
public:
myProperty(const T& initValue) : m_valueinitValue
private:
T m_value;
;
When creating myProperty
type object:
myProperty<int> ip1;
myProperty<Vec3> vp11, 2, 3;
// myProperty<Vec3> vp21, 2, 3; ERROR: myProperty doesn't have a matching constructor.
Is there an elegant way of making vp2
initialization work? Specializing myProperty
for Vec3
is an overkill.
c++ class templates constructor c++14
Having this code:
struct Vec3
int x;
int y;
int z;
;
template <typename T>
class myProperty
public:
myProperty(const T& initValue) : m_valueinitValue
private:
T m_value;
;
When creating myProperty
type object:
myProperty<int> ip1;
myProperty<Vec3> vp11, 2, 3;
// myProperty<Vec3> vp21, 2, 3; ERROR: myProperty doesn't have a matching constructor.
Is there an elegant way of making vp2
initialization work? Specializing myProperty
for Vec3
is an overkill.
c++ class templates constructor c++14
c++ class templates constructor c++14
asked Nov 10 at 10:55
nVxx
244212
244212
1
Is makingmyProperty
itself an aggregate an option? (I.e. remove the constructor and makem_value
public.) Otherwise the only solution is see is a templated constructor, that forwards its arguments to theT
constructor:template <typename ...P> myProperty(P &&... p) : m_value(std::forward<P>(p)...)
.
– HolyBlackCat
Nov 10 at 11:01
@HolyBlackCat, that won't compile. To make it work, you'll have to put extra parentheses, likem_value(std::forward<P>(p)...)
, that wayvp2
case will compile butvp1
not. And makingmyProperty
an agregate is not an option.
– nVxx
Nov 10 at 11:35
Oops. It should be: m_valuestd::forward<P>(p)...
, otherwisemyProperty<int> ip1;
doesn't compile. "vp2 case will compile but vp1 not" Does it mean you want bothvp1
andvp2
to compile, not onlyvp2
?
– HolyBlackCat
Nov 10 at 11:38
@HolyBlackCat, actually no, I don't want vp1 to compile :) My bad, didn't test your suggestion thoroughly, assumed something likevp4myVec3Objet
won't compile also, but it does, so this looks good :) Would accept the answer if you post it.
– nVxx
Nov 10 at 11:52
1
Turns out it breaks copy-construction (because it's a better match thanmyProperty(const myProperty &)
if the parameter is non-const). I'll post an answer if I figure out how to fix that in a neat way.
– HolyBlackCat
Nov 10 at 12:07
add a comment |
1
Is makingmyProperty
itself an aggregate an option? (I.e. remove the constructor and makem_value
public.) Otherwise the only solution is see is a templated constructor, that forwards its arguments to theT
constructor:template <typename ...P> myProperty(P &&... p) : m_value(std::forward<P>(p)...)
.
– HolyBlackCat
Nov 10 at 11:01
@HolyBlackCat, that won't compile. To make it work, you'll have to put extra parentheses, likem_value(std::forward<P>(p)...)
, that wayvp2
case will compile butvp1
not. And makingmyProperty
an agregate is not an option.
– nVxx
Nov 10 at 11:35
Oops. It should be: m_valuestd::forward<P>(p)...
, otherwisemyProperty<int> ip1;
doesn't compile. "vp2 case will compile but vp1 not" Does it mean you want bothvp1
andvp2
to compile, not onlyvp2
?
– HolyBlackCat
Nov 10 at 11:38
@HolyBlackCat, actually no, I don't want vp1 to compile :) My bad, didn't test your suggestion thoroughly, assumed something likevp4myVec3Objet
won't compile also, but it does, so this looks good :) Would accept the answer if you post it.
– nVxx
Nov 10 at 11:52
1
Turns out it breaks copy-construction (because it's a better match thanmyProperty(const myProperty &)
if the parameter is non-const). I'll post an answer if I figure out how to fix that in a neat way.
– HolyBlackCat
Nov 10 at 12:07
1
1
Is making
myProperty
itself an aggregate an option? (I.e. remove the constructor and make m_value
public.) Otherwise the only solution is see is a templated constructor, that forwards its arguments to the T
constructor: template <typename ...P> myProperty(P &&... p) : m_value(std::forward<P>(p)...)
.– HolyBlackCat
Nov 10 at 11:01
Is making
myProperty
itself an aggregate an option? (I.e. remove the constructor and make m_value
public.) Otherwise the only solution is see is a templated constructor, that forwards its arguments to the T
constructor: template <typename ...P> myProperty(P &&... p) : m_value(std::forward<P>(p)...)
.– HolyBlackCat
Nov 10 at 11:01
@HolyBlackCat, that won't compile. To make it work, you'll have to put extra parentheses, like
m_value(std::forward<P>(p)...)
, that way vp2
case will compile but vp1
not. And making myProperty
an agregate is not an option.– nVxx
Nov 10 at 11:35
@HolyBlackCat, that won't compile. To make it work, you'll have to put extra parentheses, like
m_value(std::forward<P>(p)...)
, that way vp2
case will compile but vp1
not. And making myProperty
an agregate is not an option.– nVxx
Nov 10 at 11:35
Oops. It should be
: m_valuestd::forward<P>(p)...
, otherwise myProperty<int> ip1;
doesn't compile. "vp2 case will compile but vp1 not" Does it mean you want both vp1
and vp2
to compile, not only vp2
?– HolyBlackCat
Nov 10 at 11:38
Oops. It should be
: m_valuestd::forward<P>(p)...
, otherwise myProperty<int> ip1;
doesn't compile. "vp2 case will compile but vp1 not" Does it mean you want both vp1
and vp2
to compile, not only vp2
?– HolyBlackCat
Nov 10 at 11:38
@HolyBlackCat, actually no, I don't want vp1 to compile :) My bad, didn't test your suggestion thoroughly, assumed something like
vp4myVec3Objet
won't compile also, but it does, so this looks good :) Would accept the answer if you post it.– nVxx
Nov 10 at 11:52
@HolyBlackCat, actually no, I don't want vp1 to compile :) My bad, didn't test your suggestion thoroughly, assumed something like
vp4myVec3Objet
won't compile also, but it does, so this looks good :) Would accept the answer if you post it.– nVxx
Nov 10 at 11:52
1
1
Turns out it breaks copy-construction (because it's a better match than
myProperty(const myProperty &)
if the parameter is non-const). I'll post an answer if I figure out how to fix that in a neat way.– HolyBlackCat
Nov 10 at 12:07
Turns out it breaks copy-construction (because it's a better match than
myProperty(const myProperty &)
if the parameter is non-const). I'll post an answer if I figure out how to fix that in a neat way.– HolyBlackCat
Nov 10 at 12:07
add a comment |
1 Answer
1
active
oldest
votes
up vote
6
down vote
A simple solution is to use a variadic template constructor:
template <typename ...P> myProperty(P &&... p) : m_valuestd::forward<P>(p)...
It makes myProperty<Vec3> vp21, 2, 3;
compile.
Also it stops myProperty<Vec3> vp11, 2, 3;
from compiling (which seems to match your intentions).
The problem with this option is that it prevents copy construction from working propertly.
(If the parameter is a non-const myProperty<T>
lvalue, then this variadic constructor is a better match than myProperty(const myProperty &)
.)
This can be solved with SFINAE:
C++17 with <experimental/type_traits>
:
#include <experimental/type_traits>
#include <utility>
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<std::experimental::is_detected_v<list_constructible, T, P...>>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
C++14:
#include <type_traits>
#include <utility>
template <typename...> using void_t = void;
template <typename DummyVoid, template <typename...> class A, typename ...B> struct is_detected : std::false_type ;
template <template <typename...> class A, typename ...B> struct is_detected<void_t<A<B...>>, A, B...> : std::true_type ;
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<is_detected<void, list_constructible, T, P...>::value>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
Yet only an upvote, to have some time to experiment with this solution and also keep the question open for alternative solutions.
– nVxx
yesterday
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
6
down vote
A simple solution is to use a variadic template constructor:
template <typename ...P> myProperty(P &&... p) : m_valuestd::forward<P>(p)...
It makes myProperty<Vec3> vp21, 2, 3;
compile.
Also it stops myProperty<Vec3> vp11, 2, 3;
from compiling (which seems to match your intentions).
The problem with this option is that it prevents copy construction from working propertly.
(If the parameter is a non-const myProperty<T>
lvalue, then this variadic constructor is a better match than myProperty(const myProperty &)
.)
This can be solved with SFINAE:
C++17 with <experimental/type_traits>
:
#include <experimental/type_traits>
#include <utility>
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<std::experimental::is_detected_v<list_constructible, T, P...>>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
C++14:
#include <type_traits>
#include <utility>
template <typename...> using void_t = void;
template <typename DummyVoid, template <typename...> class A, typename ...B> struct is_detected : std::false_type ;
template <template <typename...> class A, typename ...B> struct is_detected<void_t<A<B...>>, A, B...> : std::true_type ;
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<is_detected<void, list_constructible, T, P...>::value>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
Yet only an upvote, to have some time to experiment with this solution and also keep the question open for alternative solutions.
– nVxx
yesterday
add a comment |
up vote
6
down vote
A simple solution is to use a variadic template constructor:
template <typename ...P> myProperty(P &&... p) : m_valuestd::forward<P>(p)...
It makes myProperty<Vec3> vp21, 2, 3;
compile.
Also it stops myProperty<Vec3> vp11, 2, 3;
from compiling (which seems to match your intentions).
The problem with this option is that it prevents copy construction from working propertly.
(If the parameter is a non-const myProperty<T>
lvalue, then this variadic constructor is a better match than myProperty(const myProperty &)
.)
This can be solved with SFINAE:
C++17 with <experimental/type_traits>
:
#include <experimental/type_traits>
#include <utility>
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<std::experimental::is_detected_v<list_constructible, T, P...>>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
C++14:
#include <type_traits>
#include <utility>
template <typename...> using void_t = void;
template <typename DummyVoid, template <typename...> class A, typename ...B> struct is_detected : std::false_type ;
template <template <typename...> class A, typename ...B> struct is_detected<void_t<A<B...>>, A, B...> : std::true_type ;
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<is_detected<void, list_constructible, T, P...>::value>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
Yet only an upvote, to have some time to experiment with this solution and also keep the question open for alternative solutions.
– nVxx
yesterday
add a comment |
up vote
6
down vote
up vote
6
down vote
A simple solution is to use a variadic template constructor:
template <typename ...P> myProperty(P &&... p) : m_valuestd::forward<P>(p)...
It makes myProperty<Vec3> vp21, 2, 3;
compile.
Also it stops myProperty<Vec3> vp11, 2, 3;
from compiling (which seems to match your intentions).
The problem with this option is that it prevents copy construction from working propertly.
(If the parameter is a non-const myProperty<T>
lvalue, then this variadic constructor is a better match than myProperty(const myProperty &)
.)
This can be solved with SFINAE:
C++17 with <experimental/type_traits>
:
#include <experimental/type_traits>
#include <utility>
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<std::experimental::is_detected_v<list_constructible, T, P...>>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
C++14:
#include <type_traits>
#include <utility>
template <typename...> using void_t = void;
template <typename DummyVoid, template <typename...> class A, typename ...B> struct is_detected : std::false_type ;
template <template <typename...> class A, typename ...B> struct is_detected<void_t<A<B...>>, A, B...> : std::true_type ;
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<is_detected<void, list_constructible, T, P...>::value>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
A simple solution is to use a variadic template constructor:
template <typename ...P> myProperty(P &&... p) : m_valuestd::forward<P>(p)...
It makes myProperty<Vec3> vp21, 2, 3;
compile.
Also it stops myProperty<Vec3> vp11, 2, 3;
from compiling (which seems to match your intentions).
The problem with this option is that it prevents copy construction from working propertly.
(If the parameter is a non-const myProperty<T>
lvalue, then this variadic constructor is a better match than myProperty(const myProperty &)
.)
This can be solved with SFINAE:
C++17 with <experimental/type_traits>
:
#include <experimental/type_traits>
#include <utility>
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<std::experimental::is_detected_v<list_constructible, T, P...>>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
C++14:
#include <type_traits>
#include <utility>
template <typename...> using void_t = void;
template <typename DummyVoid, template <typename...> class A, typename ...B> struct is_detected : std::false_type ;
template <template <typename...> class A, typename ...B> struct is_detected<void_t<A<B...>>, A, B...> : std::true_type ;
template <typename T, typename ...P> using list_constructible = decltype(Tstd::declval<P>()...);
// ...
template
<
typename ...P,
typename = std::enable_if_t<is_detected<void, list_constructible, T, P...>::value>
>
myProperty(P &&... p) : m_valuestd::forward<P>(p)...
edited Nov 10 at 12:37
answered Nov 10 at 12:30
HolyBlackCat
14.6k23361
14.6k23361
Yet only an upvote, to have some time to experiment with this solution and also keep the question open for alternative solutions.
– nVxx
yesterday
add a comment |
Yet only an upvote, to have some time to experiment with this solution and also keep the question open for alternative solutions.
– nVxx
yesterday
Yet only an upvote, to have some time to experiment with this solution and also keep the question open for alternative solutions.
– nVxx
yesterday
Yet only an upvote, to have some time to experiment with this solution and also keep the question open for alternative solutions.
– nVxx
yesterday
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53238233%2finitialize-class-template-aggregate-type-member-with-aggregate-initializer-bu%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
1
Is making
myProperty
itself an aggregate an option? (I.e. remove the constructor and makem_value
public.) Otherwise the only solution is see is a templated constructor, that forwards its arguments to theT
constructor:template <typename ...P> myProperty(P &&... p) : m_value(std::forward<P>(p)...)
.– HolyBlackCat
Nov 10 at 11:01
@HolyBlackCat, that won't compile. To make it work, you'll have to put extra parentheses, like
m_value(std::forward<P>(p)...)
, that wayvp2
case will compile butvp1
not. And makingmyProperty
an agregate is not an option.– nVxx
Nov 10 at 11:35
Oops. It should be
: m_valuestd::forward<P>(p)...
, otherwisemyProperty<int> ip1;
doesn't compile. "vp2 case will compile but vp1 not" Does it mean you want bothvp1
andvp2
to compile, not onlyvp2
?– HolyBlackCat
Nov 10 at 11:38
@HolyBlackCat, actually no, I don't want vp1 to compile :) My bad, didn't test your suggestion thoroughly, assumed something like
vp4myVec3Objet
won't compile also, but it does, so this looks good :) Would accept the answer if you post it.– nVxx
Nov 10 at 11:52
1
Turns out it breaks copy-construction (because it's a better match than
myProperty(const myProperty &)
if the parameter is non-const). I'll post an answer if I figure out how to fix that in a neat way.– HolyBlackCat
Nov 10 at 12:07