Class specialization by lvalue vs. rvalue argument

I am trying to write a templated class that is specialized depending on the value type (lvalue/rvalue) of one of its constructor arguments.

The example hereunder achieves that with the use of a factory function that does the work of discriminating between the value types and then relies on an extra template parameter(bool vtype) to instantiate the desired class object.

The problem with this method is that the two varieties of the templated classes behave like two separate classes. Actually, the extra parameter could be eliminated by just giving a different name to each class. It is not possible to use the class as function parameter time, derive a class or define a variable without specifying the extra parameter.

Is there a way to get to the same result without relying on the factory function and without the use of an extra class parameter (unless it is just selector dependant on the primary type parameter in an enable_if expression or similar)?

Perhaps, what I am looking for is conceptually impossible, then I would be interested in an explanation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>

template<class T,bool vtype >
class box;

template<class T>
class box<T,true>{
  T& val;
  public:
  std::string txt{"lvalue version"};
  box(T& v):val(v){
    if (std::is_lvalue_reference<decltype(v)>::value){std::cout << "this is an lvalue" << std::endl;}
    if (std::is_rvalue_reference<decltype(v)>::value){std::cout << "this is an rvalue" << std::endl;}
  }
};

template<class T>
class box<T,false>{
  T val;
  public:
  std::string txt{"rvalue version"};
  box(T&& v):val(std::move(v)){
    if (std::is_lvalue_reference<decltype(v)>::value){std::cout << "this is an lvalue" << std::endl;}  //this code is not correct b/c  was moved
    if (std::is_rvalue_reference<decltype(v)>::value){std::cout << "this is an rvalue" << std::endl;}
  };
};

template<class T>
box<T,true> box_maker(T& v){return box<T,true>(v);}

template<class T>
box<T,false> box_maker(T&& v){return box<T,false>(std::move(v));}

int main ()
{ 
  int a{10};
  std::cout <<std::endl<<"box_maker(a)" << std::endl;
  auto h1 = box_maker(a);
  std::cout << h1.txt  << std::endl;
  
  std::cout <<std::endl<<"box_maker(10)" << std::endl;
  auto h2 = box_maker(10);
  std::cout << h2.txt  << std::endl;  
}




I found the solution! Just needed to remember that int, int& and int&& are actually different types.

I also figured out how to create a derived class. It simple yet not straightforward because it requires to use std::forward and also reference collapsing.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include "iteratorDispatch.h"

 template<class T,class X = nullptr_t >
 class box;

template<class T>
class box<T,typename std::enable_if<std::is_lvalue_reference<T>::value,nullptr_t>::type> {
  T& val;
  public:
  std::string txt{"lvalue version"};
  box(T& v):val(v){
    if (std::is_lvalue_reference<decltype(v)>::value){std::cout << "this is an lvalue" << std::endl;}
    if (std::is_rvalue_reference<decltype(v)>::value){std::cout << "this is an rvalue" << std::endl;}
  }
};

template<class T>
class box<T,typename std::enable_if<std::is_rvalue_reference<T>::value,nullptr_t>::type> {
  T val;
  public:
  std::string txt{"rvalue version"};
  box(T&& v):val(std::move(v)){
    if (std::is_lvalue_reference<decltype(v)>::value){std::cout << "this is an lvalue" << std::endl;}  //this code is not correct b/c  was moved
    if (std::is_rvalue_reference<decltype(v)>::value){std::cout << "this is an rvalue" << std::endl;}
  };
};

template<class T>
box<T&> box_maker(T& v){return box<T&>(v);}

template<class T>
box<T&&> box_maker(T&& v){return box<T&&>(std::move(v));}

template<class T>
class crate: public box<T>{
  public:
  crate(T&& v):box<T>(std::forward<T>(v)){}
};

int main ()
{ 
  int a{10};
  std::cout <<std::endl<<"box_maker(a)" << std::endl;
  auto h1= box_maker(a);
  std::cout << h1.txt  << std::endl;
  
  std::cout <<std::endl<<"box_maker(10)" << std::endl;
  auto h2= box_maker(10);
  std::cout << h2.txt  << std::endl;  

  
  std::cout <<std::endl<<"crate<int&>(a)" << std::endl;  
  auto x = crate<int&>(a);
  std::cout << x.txt  << std::endl; 
   std::cout << a  << std::endl; 
   
   std::cout <<std::endl<<"crate<int&&>(77)" << std::endl;  
   auto y = crate<int&&>(77);
   std::cout << y.txt  << std::endl; 
  
}
Topic archived. No new replies allowed.