A Laser-Focused Alternative to std::decay Using C++14’s Alias Templates

In the previous blog post, there was some code iterating through a generic container, using a range-for loop like so:

for (const auto& elem : c)

I wanted the C++ compiler to pick a specific traits class template specialization based on the current element’s type. Using just decltype(elem) turned out to be a bug, since the returned type was a const reference to the element’s type, but what I actually wanted was the original element’s type (not a const reference to it).

So, I used std::decay to strip off the “const &” part from the type returned by decltype(elem), leaving only the actual element’s type, such that the compiler could pick the proper traits class specialization based on that type.

However, std::decay performs also additional transformations, like those involving arrays and functions, which aren’t relevant in our case.

A more laser-focused alternative would be using std::remove_reference and std::remove_const (both defined in the <type_traits> standard header).

We could define a simple class template for that purpose:

#include <type_traits> // for remove_const, remove_reference

// Strip off "const &" from "const Type&",
// leaving only "Type".
template <typename T>
class RemoveConstReference
{
private:
  // Strip off the reference part
  typedef typename 
    std::remove_reference<T>::type 
    TypeWithoutRef;

public:

  // Strip off the const,
  // after having stripped off the reference
  typedef typename 
    std::remove_const<TypeWithoutRef>::type 
    type;
};

And then that RemoveConstReference custom-defined template could be applied to strip off “const &” from elem’s type:

typedef 
  RemoveConstReference<decltype(elem)>::type
  ElemType;

And finally the correct traits specialization is picked using code like this:

const int x = 
  Traits<ElemType>::GetSomeProperty(elem);

In addition, C++14 introduced some convenient alias templates. For example: besides C++11’s remove_reference, in C++14 a remove_reference_t alias template was defined:

template <typename T>
using remove_reference_t
  = typename remove_reference<T>::type;

This is basically syntactic sugar, a syntactic shortcut.

Similarly, there’s a C++14 remove_const_t alias template matching C++11’s remove_const.

Using those _t-ending alias templates, it’s possible to further simplify the code to strip off the “const&”, composing remove_const_t and remove_reference_t like this (“std::” prefix omitted for the sake of clarity):

typedef remove_const_t<
  remove_reference_t<
    decltype(elem)>> 
ElemType;

We could even define a custom reusable alias template just for removing “const &”:

template <typename T>
using RemoveConstReference_t = 
  std::remove_const_t<
    std::remove_reference_t<T>>;

And then use it like so:

typedef RemoveConstReference_t<decltype(elem)> 
  ElemType;

const int x = 
  Traits<ElemType>::GetSomeProperty(elem);

These C++14’s alias templates, which are just “syntactic sugar”, are nonetheless convenient to make C++ code simpler and more clear!

 

Mixing Traits Classes, Range-For Iteration and decltype: A Subtle Bug

Suppose you have defined a traits class to query a property for some type. This is a common technique used in C++: for example, to get the largest value of the type int, you can call std::numeric_limits<int>::max. std::numeric_limits is a class template that is specialized for arithmetic types like int, double, etc.

In the int specialization (numeric_limits<int>), its max method returns the largest value of the type int; in the double specialization (numeric_limits<double>), its max method returns the largest value of the type double, and so on. In generic code, when you have some arithmetic type T, you can query T’s largest value calling numeric_limits<T>::max.

So, you defined some traits class, which is a class template, that exposes some public static methods to query some properties for some types. For example:

 

template <typename Type>
struct Traits
{
  static int GetSomeProperty(const Type&)
  {
    cout << "Generic"; 
    return 0;
  }
};

(Of course, in production code, traits classes can contain more than a single method, just like the numeric_limits traits class exposes several member functions.)

Then you specialize the above traits class for the std::string type:

template <>
struct Traits<string>
{
  static int GetSomeProperty(const string&)
  { 
    cout << "String"; 
    return 1; 
  }
};

This property might be the length in bytes of the input parameter, or it can be some other data associated to that parameter; the description of the particular property is unimportant here.

Then you have a function template that iterates through some generic container using a range-for loop and inside that loop, you invoke the Traits::GetSomeProperty method to query the given property of the current element:

template <typename Container>
void DoSomething(const Container & c)
{
  for (const auto& elem : c)
  {
    cout << elem << ": ";

    int x = Traits<decltype(elem)>::
      GetSomeProperty(elem);

    cout << '\n';
  }
}

Since this function operates on a generic container, you don’t know a priori the type of the elements in the container. So you think of using decltype(elem) to get the type of the current element.

Now, if you invoke that function template on a vector<string>, like this:

int main()
{
  vector<string> test{ "Hello", "Connie" };
  DoSomething(test);
}

What you would expect as output is probably:

Hello: String
Connie: String

Right? That’s because the DoSomething function template is iterating through a vector of strings, so decltype(elem) seems to be std::string, and so Traits<decltype(elem)> would pick Traits<string>, and consequently the Traits<string>::GetSomeProperty specialization would be invoked, which should print “String”.

Well, if you run this code, what you actually get is:

Hello: Generic
Connie: Generic

Why is that? Why is “Generic” printed instead of the expected “String”?

If pay attention to the range-for loop iteration:

    for (const auto& elem : c)

you’ll notice that elem’s type is actually not std::string. In fact, elem is a const reference to a std::string. That’s because of the “const auto&” iteration format, which in this particular case of vector<string> becomes “const std::string&”.

So, decltype(elem) is not std::string: it’s actually a “const std::string&”, that is: a const reference to a std::string. Since there is no Traits<T> specialization for a “const reference to a std::string” (in fact, you only have a specialization for a std::string), the generic Traits<T> class template code is picked by the compiler, so the generic Traits<T>::GetSomeProperty method is invoked, which prints “Generic”.

How can you fix this code? How to remove the “const &”, leaving only the “std::string” part?

Well, an option is to use std::decay:

decay<decltype(elem)>::type

This is std::string, without the const reference part.

A more verbose alternative to strip that off is remove_reference and remove_const (thanks Mr.STL for the tip on that one).

So, inside the range-for loop, this line will do The Right Thing (TM):

int x = 
  Traits<std::decay<decltype(elem)>::type>::
  GetSomeProperty(elem);

If the above line seems too hard to parse, it’s possible to break it into pieces for better readability, with the help of a convenient intermediate typedef:

typedef std::decay<decltype(elem)>::type
  ElemType;

int x = 
  Traits<ElemType>::GetSomeProperty(elem);

I hope this blog post will spare you some headache and debugging time if you have a range-for loop iterating with “const auto&”, and you are stuck getting the wrong (generic) traits invoked instead of the expected specialization.

Some compilable code is available here on GitHub for download and experimentation.

Enabling the StrSafe Locale Functions

The Windows SDK defines some string functions that provide special processing for buffer handling, with the goal of reducing security issues that involve buffer overruns. These functions are defined in the <StrSafe.h> header. If you are unfamiliar with them, a quick introduction can be found here.

Some of these functions include a parameter for locale information. These locale-aware StrSafe functions have an _l suffix, for example: StringCbPrintf_l.

However, if you try to use the aforementioned function in your Windows C++ code after including <StrSafe.h>, the compiler will complain with an error message like:

error C3861: ‘StringCbPrintf_l’: identifier not found

After some spelunking in the gigantic <StrSafe.h> header with the help of some search tool, you will discover that these locale-aware functions are excluded by default, and you have to explicitly enable them, #defining the preprocessor macro STRSAFE_LOCALE_FUNCTIONS.

StrSafe Locale Aware Functions Disabled by Default
StrSafe Locale-Aware Functions Disabled by Default

This doesn’t seem mentioned in the MSDN documentation for these functions (at least, I was unable to find a note about that).

I would have preferred a different policy of enabling them by default, and if for some reasons these functions would conflict with some existing code bases, those could be disabled defining a macro like STRSAFE_NO_LOCALE_FUNCTIONS (just like the existing approach of explicitly disabling functions with the STRSAFE_NO_CB_FUNCTIONS and STRSAFE_NO_CCH_FUNCTIONS macros).