0

In a c++11 project I have some structs like those:

#include <type_traits>

template <bool B, class T = void> using disable_if_t = typename std::enable_if<!(B), T>::type;

enum class disable {};

template <int A> struct StructA
{
    using sa = StructA<A>;

    static constexpr int a = A;
    static constexpr int b = 1;
};

template <typename SA, int B> struct StructB
{
    using sa = typename SA::sa;

    static constexpr int a = sa::a;
    static constexpr int b = B;
};

template <typename SB, typename = int> struct disable_first : std::integral_constant<bool, SB::a == 0 && SB::b == 1>
{
};

template <typename> struct disable_second : std::false_type
{
};

template <typename, typename> class StructC;
template <typename SB, typename T2> struct disable_second<StructC<SB, T2>> : std::true_type
{
};

template <typename SB, typename T> struct StructC
{
    template <typename T2, disable_if_t<disable_first<SB, T2>{} || disable_second<T2>{}, disable>...> explicit StructC(T2 v) noexcept;
    template <typename SB2, typename T2> StructC(const StructC<SB2, T2>& v) noexcept;
    template <typename SB2, typename T2> StructC(StructC<SB2, T2>&& v) noexcept;
};

template <typename T, typename T2> void Func(StructC<StructA<1>, T2> b);

int main()
{
    StructC<StructB<StructA<1>, 1>, int> x(40);
    Func<float>(x);

    return 0;
}

Now I basically have two quesions:

  1. Can the StructC(const StructC<SB2, T2>& s) constructor used as copy-constructor (same counts for StructC(StructC<SB2, T2>&& s) for move constructing)

  2. How can I say the compiler, that it's totally OK to call Func with StructB<StructA<1>, 1> and int although it requires StructA<1> and float without explicitly calling the constructor? The constructor of StructC is totally capable of converting between those.

Edit 1: provide an example which fails correctly.

Uroc327
  • 1,379
  • 2
  • 10
  • 28
  • 1
    [Your code compiles](http://rextester.com/RMPNIO4554) once a default constructor for `StructC` is added (needed for `StructC, float> a;`). Exactly what constructor do you feel gets ignored, and by whom? – Igor Tandetnik Feb 15 '15 at 16:21
  • Seems I screwed up my minimal example, thanks.. Will provided a new one. I don't want to throw the complete code at you. – Uroc327 Feb 15 '15 at 16:47
  • 1
    The `Func` call fails during template argument deduction; the presence or absence of constructors is irrelevant to this step, as user-defined conversions are not considered. If you bypass this step by fully specifying template arguments, as in `Func(x);` then [the code compiles](http://rextester.com/VZJ88377) (well, progresses to linker errors). There is no way for the compiler to guess, looking at `StructC`, that `T2` in a seemingly-unrelated `StructC` is supposed to stand for `int`. – Igor Tandetnik Feb 15 '15 at 18:35
  • The actual function has some more template arguments. Is it possible, to specify only the `int` and let the compiler guess the rest? (as the compiler can guess everything else atm) – Uroc327 Feb 15 '15 at 18:55
  • Yes, you can specify any number of parameters (from the first one on, sequentially) and let the compiler try and deduce the rest. – Igor Tandetnik Feb 15 '15 at 19:42

1 Answers1

2

Can the StructC(const StructC<S1, T1>& s) constructor used as copy-constructor (same counts for StructC(StructC<S1, T1>&& s) for move constructing)

A copy or move constructor cannot be a template. StructC will have an implicitly declared copy constructor and an implicitly declared move constructor. When you copy or move the object, the implicitly declared constructors will participate in overload resolution with your constructor templates; if they are equally as good (which appears to be the case in your code), the implicitly declared non-template constructor will be preferred.

Note that copy elision (including (named) return value optimization) cannot be performed if overload resolution for the "copy/move" operation selects your constructor template rather than an actual copy/move constructor. This may impact performance.

How can I say the compiler, that it's totally OK to call Func with StructA<1, 2, 3> and float although it requires StructB<4, 5> and double without explicitly call the constructor? The constructor of StructC is totally capable of converting between those.

Your current constructor template already defines an implicit conversion from every kind of StructC to every other kind of StructC. Your code should compile once a default constructor is added to StructC.

Community
  • 1
  • 1
T.C.
  • 133,968
  • 17
  • 288
  • 421