Why Use Empty Structures?

closed account (zb0S216C)
 
template <typename T> struct Ref { };

Every so often, I come across code that uses empty structures, like the one above, for all sorts of stuff. In one scenario, I've seen them used as part of a conversion operator.

What possible uses could an empty structure have? Could someone provide and example or two of their use?

Thanks in advance.

Wazzak
Last edited on
Perhaps the most widely used technique involving empty "tag" structures is tag dispatching. For an explanation and an example, see:
http://www.boost.org/community/generic_programming.html#tag_dispatching

In general, it can be used for method colouring. For example, an empty structure to colour the constructor is commonly used in the implementation of the prototype (exemplar) pattern:

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
struct base
{
    virtual ~base() ;

    // other polymorphic operations
    // virtual .... = 0 ;
    
    virtual void save( std::ostream& stm ) const = 0 ;

     // construct instance using chain of responsibility
    static base* create_instance( const std::string& id ) ; // create based on id
    static base* create_instance( const std::istream& stm ) ; // create based on data in stream

   protected:

       virtual base* create( std::string& id ) const = 0 ;
       virtual base* create( std::istream& stm ) const = 0 ;

       // constructors for instance objects
       base() ;
       base( const std::string& ) ;

       // constructor for protypical objects
       struct for_exemplar {} ;
       base( for_exemplar  ) ;

   private: std::string _id ;
};

closed account (zb0S216C)
Thanks for the reply, JLBorges :)

Both Method Colouring & Tag Dispatching are foreign terms to me. I found information (including the information you provided) in regards to tag dispatching, but none regarding method colouring. What do you mean by "colouring the constructor"?

Wazzak
Let us say, for some reason (which is uninteresting for the purposes of this discussion), we need two different implementations of a particular function which takes an int and a double as input. This is easy; we can give each function a different name:
1
2
void foo( int, double ) ;
void foo_special_case( int, double ) ;


However, there are certain operations for which we can't have two different names; for instance overloaded operators and constructors. If we want to write a class which has two different constructors, both of which logically needs an int and a double as input, the foo/foo_special_case technique won't work; so we 'colour' the constructor with an extra parameter which is not named and is not used:
1
2
3
4
5
6
7
struct A
{
    A( int count, double ratio ) ;
    
    struct for_special_occasions {} ;
    A( int count, double ratio, for_special_occasions /* not used */ ) ;
}; 


Tag dispathing uses function colouring to determine which template function is to be instantiated.
Last edited on
wrt to method colouring - I am aware of the prodedure - but I have never heard
it called any particular name.
closed account (zb0S216C)
So let me get this straight, tagging is used to identify an ambiguous overload of a function, operator, or constructor?

Wazzak
> tagging is used to identify an ambiguous overload of a function, operator, or constructor?

A tag type can be used to distinguish between overloads of a function, operator, or constructor that would otherwise have been ambiguous.
closed account (zb0S216C)
OK, simple enough. Thanks, JLBorges :) I appreciate it :)

Wazzak
In the same vein of 'resolving ambiguity' - there is also this scenario
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <typename T>
 struct Some_Struct
 {
     typedef T  Type; //some typedef
 };
 
template<typename T>
void functionX ( typename Some_Struct<T>::Type param ) //See below
{
    //do stuff
}


int main()
{
    //The following cannot be instantiated because  parameters
	//of of the form typename Some_Struct<T>::inner_Type do
	//not take part in template parameter deduction -so we will
	//need another parameter that can be deduced
    
	functionX( int() );  //Template parameter deduction failuire
}


//------------------------------------------------------------
//--------------------------------------------------------------
//A way to resolve the above issue
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
template <typename T>
struct  Helper
{
    
};

template <typename T>
 struct Some_Struct
 {
     typedef T  Type; //some typedef
 };
 
 //We add a simple parameter to the function
 //it won't be used - but it is in a form
 //where the template parameter can be deduced
template<typename T>
void functionX ( typename Some_Struct<T>::Type param , Helper<T> ) 
{
    //do stuff
}

int main()
{   
    functionX( int(), Helper<int>() );
}


closed account (zb0S216C)
Thanks for the additional information, guestgulkan :) I'll look into template parameter deduction further.

Thanks again :)

Wazzak
Topic archived. No new replies allowed.