Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
399 views
in Technique[技术] by (71.8m points)

c++ variadic template constructor and common constructors

Code like (c++14):

struct S { int a; int b; };

class C
{
  public:
    C(char const*, size_t) {} // 1
    C(S const&) {} // 2
    C(S const*) {} // 3
    template<typename ...T> C(T&& ...) {} // 4

 // C(S) {} // 5
 // C(S*) {} // 6
};

S s { 1, 2 };
C c1 { s }; // calls 4 and not 2
C c2 { "abc", 3 }; // calls 4 and not 1
C c3 { (char const*)"abc", (size_t)3 }; // calls 1 - ok
C c4 { s }; // calls 5 if uncommented
C c5 { &s }; // calls 6 if uncommented
S const s2 {};
C c6 { &s2 }; // calls 3

Simple constructor is called if it has exact the same signature as the passed parameter. Is there some trick to use common constructors as usual with a variadic template constructor, without copying classes, passed as parameters, and overloading constructors like:

C(S const*) {}
C(S*) {}

And without additional tags in constructors

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Create two tiers of constructor. Then tag dispatch.

template<template<class...>class Z, class T>
struct is_template:std::false_type{};
template<template<class...>class Z, class...Ts>
struct is_template<Z, Z<Ts...>>:std::true_type{};

struct foo {
private:
  template<class T> struct tag{ explicit tag(int) {} };
public:
  foo( tag<std::true_type>, const char*, size_t );
  template<class...Ts>
  foo( tag<std::false_type>, Ts&&...ts );

public:
  foo() = default; // or whatever
  template<class T0, class...Ts,
    std::enable_if_t<!is_template<tag, std::decay_t<T0>>{},int> =0>
  foo(T0&&t0, Ts&&...ts):
    foo( tag<typename std::is_constructible< foo, tag<std::true_type>, T0&&, Ts&&... >::type>{0}, std::forward<T0>(t0), std::forward<Ts>(ts)... )
  {}
};

The "preferred" ctors are prefixed with std::true_type, the "less preferred" ctors are prefixed with std::false_type.

This has the usual imperfections of perfect forwarding. If you take initializer lists, you'll want to have another "public" ctor that takes that explicitly, for example. And function name argument magical overloading won't work. NULL is an int. Etc.

You can imagine a version that, instead of having two tiers, has an arbitrary number. The is_constructible< ... > clause in the public facing ctor instead is replaced with some magic that finds the highest N such that tag<N>, blah... can construct the object (or, lowest N, whichever way you want to do it). Then it returns the type tag<N>, which then dispatches to that tier.

Using a technique like this:

template <typename... T,
      typename = std::enable_if_t<!std::is_constructible<C, T&&...>::value>
       >
C(T&&... ) { }

runs into a serious problem down the road, as we have instantiated is_constructible in a context where it gets the answer wrong. And in practice, compilers cache the results of template instantiations, so now the result of is_constructible is compiler order dependent (ODR violation I suspect).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...