The best way to handle many conditional compilation conditions

I have a template class that has a method in which template parameters define the input and output of this method as follows:

template <typename In, typename Out>
class Foo
{
    Out fn(const In& in)
    {
        Out out;
        return out;
    }
}

So, I tried this, but then (probably obvious) errors occur when trying to use voidfor Inor Out. Therefore, I tried to add several methods that are variations of this topic, hoping that their replacements will include the corresponding functions and disable the invalid ones:

template <std::enable_if_t<std::is_void<InputType>::value>* = nullptr>
OutputType fn()
{
    OutputType out;
    return out;
}

template <std::enable_if<(!std::is_void<OutputType>::value) && (!std::is_void<InputType>::value)>* = nullptr>
OutputType fn(InputType& t)
{
    OutputType out;
    return out;
}

template <std::enable_if<std::is_void<OutputType>::value>* = nullptr>
void fn(InputType& t)
{}

Which forces me to return to the territory of the "invalid reference to the void" or with counter signatures.

How should I correctly handle these conditions so that only one of the following signatures is created from the template:

/*In == void && Out != void*/
Out fn(/* no input here to keep compiler happy*/) { return Out; }

/*In != void && Out != void, standard case*/
Out fn(const In& in) { return Out; }

/*In != void && Out == void*/
void fn(const In& in) { /* No returns here to keep compiler happy*/; }
+4
2

Foo , In Out , , .

( Foo, Foo)

template<typename Out>
struct Foo<void, Out>  // specialisation for only In = void
{ ... };

template<typename In>
struct Foo<In, void>   // specialisation for only Out = void
{ ... };

template<>
struct Foo<void, void> // specialisation for both In and Out = void
{ ... };

:

#include <iostream>

// primary class template
template <typename In, typename Out>
struct Foo {
    Out fn(const In& in) { return Out{}; }
};

// partial specialisation for when In=void
template<typename Out>
struct Foo<void, Out> {
    Out fn() { return Out{}; }
};

// partial specialisation for when Out=void
template<typename In>
struct Foo<In, void> {
    void fn(const In& in) { }
};

// explicit specialisation for when both In=void and Out=void
template<>
struct Foo<void, void> {
    void fn() { }
};

int main() {
    Foo<int, double> f;
    f.fn(5);

    Foo<void, void> g;
    g.fn();

    Foo<void, int> h;
    h.fn();

    Foo<int, void> i;
    i.fn(5);

    return 0;
}
+5

, .

, : Out, next In.

, In

template <typename Out, typename ... Ins>
struct Foo { /* ... */ };

void ( ).

, ; , /, .

void

return (Out)someVal;

someVal Out, Out void, (: int), Out - void.

, deVoid

template <typename T>
struct deVoid
 { using type = T; };

template <>
struct deVoid<void>
 { using type = int; }; // a fake not-void type

template <typename T>
using deVoid_t = typename deVoid<T>::type;

Out

  deVoid_t<Out> out {};

return

  return (Out)out;

, Out void.

, Foo

template <typename Out, typename ... Ins>
struct Foo
 {
   Out fn (Ins const & ... ins)
    {
      deVoid_t<Out> out {};

      return (Out)out;
    }
 };

(ex void ) void.

template <typename T>
struct deVoid
 { using type = T; };

template <>
struct deVoid<void>
 { using type = int; }; // a fake not-void type

template <typename T>
using deVoid_t = typename deVoid<T>::type;

template <typename Out, typename ... Ins>
struct Foo
 {
   Out fn (Ins const & ... ins)
    {
      deVoid_t<Out> out {};

      return (Out)out;
    }
 };

int main ()
 {
   Foo<int, int>         f; f.fn(42);
   Foo<void>             g; g.fn();
   Foo<int>              h; h.fn();
   Foo<void, int>        i; i.fn(42);
   Foo<void, int, long>  j; j.fn(42, 84L);
 }
+1

Source: https://habr.com/ru/post/1688477/


All Articles