Casting template argument


M

Marcel Müller

I want to cast a template argument. Unfortunately gcc creates an error
as if the cast has never been there. Is this not allowed generally?


-----
#include <string.h>

// Sorted vector of T with the key type K using the comparer C.
template <class T, class K, int (*C)(const T* e, const K* k)>
class sorted_vector
{public:
// Find an element by it's key.
// The function will return NULL if no such element is in the container.
T* find(const K* key) const;
// Ensure an element with a particular key.
// This will either return a reference to a pointer to an existing object
// which equals to key or a reference to a NULL pointer which is
// created at the location in the container where a new object with key
// should be inserted.
T*& get(const K* key);
// Erase the element which equals key and return the removed pointer.
// If no such element exists the function returns NULL.
T* erase(const K* key);
};

// data structure, POD
struct data
{ char Key[10];
char Value[10];
};

// Works
int (*const comparer)(const data*, const char*)
= (int (*)(const data*, const char*))&strcmp;

// does not work, see below
typedef sorted_vector<data, char,
(int (*)(const data*, const char*))&strcmp> stringmap;
-----

test6.cpp:34: error: could not convert template argument `strcmp' to `int
(*)(const data*, const char*)'


Of cource, strcmp does not compare against objects of type data, but
since data is a POD type the equivalence of const data* and const char*
should not raise serious problems, and a helper function should be
superfluous.


Marcel
 
Ad

Advertisements

V

Victor Bazarov

I want to cast a template argument. Unfortunately gcc creates an error
as if the cast has never been there. Is this not allowed generally?

Yep. Not allowed.

The requirement is that a non-type template argument is expected to be
the address of a function, "expressed as & id-expression where the & is
optional if the name refers to a function or array". That means that
the "explicit type conversion expressions" are *not allowed*. Bummer.

What you perhaps need is to supply your pointer expression (by just
using the name of the function) and hope that the compiler performs the
necessary conversion itself. Standard, [temp.arg.nontype]/5 explains
what conversions are applied:
<<
The following conversions are performed on each expression used as a
non-type template-argument. If a non-type template-argument cannot be
converted to the type of the corresponding template-parameter then
the program is ill-formed.
— for a non-type template-parameter of integral or enumeration type,
integral promotions (4.5) and integral conversions (4.7) are applied.
— for a non-type template-parameter of type pointer to object,
qualification conversions (4.4) and the array-to-pointer conversion
(4.2) are applied. [Note: In particular, neither the null pointer
conversion (4.10) nor the derived-to-base conversion (4.10) are applied.
Although 0 is a valid template-argument for a non-type
template-parameter of integral type, it is not a valid template-argument
for a non-type template-parameter of pointer type. ]
— For a non-type template-parameter of type reference to object, no
conversions apply. The type referred to by the reference may be more
cv-qualified than the (otherwise identical) type of the
templateargument. The template-parameter is bound directly to the
template-argument, which must be an lvalue.
— For a non-type template-parameter of type pointer to function, only
the function-to-pointer conversion (4.3) is applied. If the
template-argument represents a set of overloaded functions (or a pointer
to such), the matching function is selected from the set (13.4).
— For a non-type template-parameter of type reference to function, no
conversions apply. If the template-argument represents a set of
overloaded functions, the matching function is selected from the
set (13.4).
— For a non-type template-parameter of type pointer to member function,
no conversions apply. If the template-argument represents a set of
overloaded member functions, the matching member function is
selected from the set (13.4).
— For a non-type template-parameter of type pointer to data member,
qualification conversions (4.4) are applied.>>

Your case seems to fall into the fourth bullet point. What you will
likely to encounter is another compiler error since the function is not
of the type expected by the compiler. You need a wrapper function.
-----
#include <string.h>

// Sorted vector of T with the key type K using the comparer C.
template <class T, class K, int (*C)(const T* e, const K* k)>
class sorted_vector
{public:
// Find an element by it's key.
// The function will return NULL if no such element is in the container.
T* find(const K* key) const;
// Ensure an element with a particular key.
// This will either return a reference to a pointer to an existing object
// which equals to key or a reference to a NULL pointer which is
// created at the location in the container where a new object with key
// should be inserted.
T*& get(const K* key);
// Erase the element which equals key and return the removed pointer.
// If no such element exists the function returns NULL.
T* erase(const K* key);
};

// data structure, POD
struct data
{ char Key[10];
char Value[10];
};

// Works
int (*const comparer)(const data*, const char*)
= (int (*)(const data*, const char*))&strcmp;

"Works" - what do you mean? You have a C-style cast here. Whether it
achieves what you expect is not known until you actually try invoking
that function using that 'comparer' pointer.
// does not work, see below
typedef sorted_vector<data, char,
(int (*)(const data*, const char*))&strcmp> stringmap;

Have you tried without the cast? What do you get?
-----

test6.cpp:34: error: could not convert template argument `strcmp' to `int
(*)(const data*, const char*)'


Of cource, strcmp does not compare against objects of type data, but
since data is a POD type the equivalence of const data* and const char*
should not raise serious problems, and a helper function should be
superfluous.

<shrug> You're trying to cheat the system by using the C-style cast.
That's *usually* an invitation for UB.

V
 
M

Marcel Müller

I want to cast a template argument. Unfortunately gcc creates an error
as if the cast has never been there. Is this not allowed generally?

Yep. Not allowed. [...]
Your case seems to fall into the fourth bullet point. What you will
likely to encounter is another compiler error since the function is not
of the type expected by the compiler. You need a wrapper function.

What a pity.
Unfortunately I need a dozen wrappers, because I have different data
types that differ only in the value type. This can be easily coded by a
template function but it is not that pretty.
"Works" - what do you mean? You have a C-style cast here. Whether it
achieves what you expect is not known until you actually try invoking
that function using that 'comparer' pointer.

Following the Rules of POD types, the pointer to the structure and to
the first structure element should be the same. And as far as the
strings are null terminated i see no problem.
In fact I showed not the excerpt of my code, but a reduced one for
demonstration purpose only. The real implementation of sorted_vector<>
internally works only with void* to prevent code size explosions. The
function is cast to int (*)(const void*, const void*). The core worker
knows nothing about the real data type. sorted_vector is a type safe
wrapper. This has been working for many years.

Have you tried without the cast? What do you get?

Exactly the same message. In this case I did not expect anything else.

<shrug> You're trying to cheat the system by using the C-style cast.
That's *usually* an invitation for UB.

Well, the application is platform dependent for other, functional
reasons anyway. The reinterpret cast from const data* to const char* is
AFAIK no UB. But this does not automatically apply to casting the
function pointer to a function with another argument type. On my target
platform it makes no difference. All pointers arguments except for
member function pointers are treated in the same way. So what is UB in
general is not necessarily UB on this specific platform.


Marcel
 
Ad

Advertisements

M

Marcel Müller

Undefined behavior arises from valid code constructs for which the C++
language definition does not impose any requirements. That doesn't
depend in any way on the compiler that you're using or on the target
platform.

This is UB with respect to the C++ language. The documentation of a
specific compiler may define the behavior in cases where the C++
language does not.
Passing pointer arguments is exactly defined on my platform. So the
result of my cast is defined behavior, by all means with the three
compilers that are supported (IBM, gnu, Watcom).
The *consequences* of using code whose behavior is undefined depend on
the compiler and the platform.

Some code behavior is even undefined if you exactly know the platform
and the compiler. E.g. accessing objects after they have been deleted.


Marcel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top