tags:

views:

104

answers:

3

I have a dilemma. Suppose I have a template class:

template <typename ValueT>
class Array
{
public:
    typedef ValueT ValueType;
    ValueType& GetValue()
    {
        ...
    }
};

Now I want to define a function that receives a reference to the class and calls the function GetValue(). I usually consider the following two ways:

Method 1:

template <typename ValueType>
void DoGetValue(Array<ValueType>& arr)
{
    ValueType value = arr.GetValue();
    ...
}

Method 2:

template <typename ArrayType>
void DoGetValue(ArrayType& arr)
{
    typename ArrayType::ValueType value = arr.GetValue();
    ...
}

There is almost no difference between the two methods. Even calling both functions will look exactly the same:

int main()
{
    Array<int> arr;
    DoGetValue(arr);
}

Now, which of the two is the best? I can think of some cons and pros:

Method 1 pros:

The parameter is a real class not a template, so it is easier for the user to understand the interface - it is very explicit that the parameter has to be Array. In method 2 you can guess it only from the name. We use ValueType in the function so it is more clear this way than when it is hidden inside Array and must be accessed using the scope operator.

In addition the typename keyword might be confusing for many non template savvy programmers.

Method 2 pros:

This function is more "true" to its purpose. When I think if it, I don't really need the class to be Array. What I really need is a class that has a method GetValue and a type ValueType. That's all. That is, this method is more generic.

This method is also less dependent on the changes in Array class. What if the template parameters of Array are changed? Why should it affect DoGetValue? It doesn't really care how Array is defined.

Evey time I have this situation I'm not sure what to choose. What is your choice?

+4  A: 

The second one is better. In your "pros" for the first one, you say, "it is very explicit that the parameter has to be Array". But saying that the parameter has to be an Array is an unnecessary limitation. In the second example, any class with a suitable GetValue function will do. Since it's an unnecessary limitation, it's better to remove it (second one) than to make it explicit (first one). You'll write more flexible templates, which is useful in future when you want to get a value from something that isn't an Array.

Steve Jessop
Generally I agree, I myself tend to go with the second option, but in a closed ecosystem where I actually want to enforce usage of Array, this limitation can become an advantage.
FireAphis
Yes, possibly I should add some equivocation - when I say "better", I mean it's the first choice unless there's some specific factor, not stated in the question, which makes something else better in a particular case. For instance if you're using 15 different functions of Array, and you exect Array's interface to change a lot during development, and for DoGetValue to always be changed to match, then letting people use DoGetValue with another class means they will have to keep changing the other class too, which is probably worse than making them use an Array.
Steve Jessop
@FireAphis, if you want to limit to Array (i can imagine reducing the chance of having ambiguities is a good reason, when what you overload is an operator), then why not use the best of two worlds: Accept Array<T>, but still use ::ValueType
Johannes Schaub - litb
A: 

My friend proposed two more, somewhat more extreme, methods:

Method 3: gives you the ability of using types that don't have a ::ValueType.

template <typename ArrayType, typename ValueType = ArrayType::ValueType>
void DoGetValue(ArrayType& arr)
{
    ValueType value = arr.GetValue();
    ...
}

Method 4: a cool way of forcing the array to be a class that has one template parameter.

template <template <typename> class ArrayType, typename ValueType>
void DoGetValue(ArrayType<ValueType>& arr)
{
    ValueType value = arr.GetValue();
    ...
}
FireAphis
Method 3 requires `typename ArrayType::ValueType`
UncleBens
Why would you want to restrict the number of parameters? That looks like shooting yourself in the foot.
Potatoswatter
@Potatoswatter, you are right, of course. These solutions are here more out of the academic interest :)
FireAphis
+1  A: 

If your function is very specific to ArrayType, and no other template will satisfy its interface requirements, use #1 as it's both shorter and more specific: the casual reader is informed that it operates on an ArrayType.

If there's a possibility that other templates will be compatible with DoGetValue, use #2 as it's more generic.

But no use obsessing, since it's easy enough to convert between them.

Potatoswatter
I like the "no use obsessing" part :)
FireAphis