开发者

implicit conversion operator hiding rules

The only way I can see to introduce my question, is by providing an example first:

template<typename T>
class foo
{
    public:
        foo()
        {}

        foo(const foo&)
        {}
};

template<template<typename> class C>
class convertible_to
{
    public:
        template<typename T>
        operator C<T> ();
};

class convertible_to_foo : public convertible_to<foo>
{
        public:
        template<typename T>
        operator foo<T>()
        {
            return foo<T>();
        }
};

I would expect that the implicit conversion operator to foo declared in convertible_to_foo would hide, i.e. overload, the implicit conversion operator to foo declared in convertible_to, but GCC 4.6 fails to accept the following lines

convertible_to_foo f;
foo<int> ff(开发者_如何学Pythonf);

and complains that the conversion from convertible_to_foo to foo<int> is ambiguous. Is this the expected behavior or GCC might be wrong here?

Thank you for reading this question.

EDIT

To understand why I would like to use such an apparently weird technique, please refer to my comment below directed at karrek and take a look at the use case below:

consider the following classes:

template<typename TYPE>
class real;

template<typename TYPE>
class complex;

which I am designing in a way to minimize exceptions due to value conditioning, i.e. domain errors. For instance, instead of allowing an error to occur, applying the function sqrt to an object of the class real will always return an object of type complex.

So far so good, but now it would be a good idea to have some pre-defined constants, like pi or the complex i. The simplest way would obviously be to declare them as follows:

real<double> pi = 3.14...;

But then, as a (maybe too) perfectionist programmer, I realize that this approach has a couple of problems:

1 - An application which requires a high precision pi, for instance, would not benefit from using real

2 - An application which is concerned about memory usage would not be able to use this pi, since operating it with an object of type real would yield an object of type real. (Alright, explicitly forcing conversions down to real every time an operation occurs is an ugliness I want to avoid)

The smartest way I see to solve this, is to design constants which lazily properly evaluate themselves through implicit conversion operators:

template<template<typename> class C>
class scalar_constant
{
    public:
        scalar_constant& operator = (const scalar_constant&) = delete;

        template<typename T>
        operator C<T> () const;
};

class pi_t : public scalar_constant<real>, public scalar_constant<complex>
{
    public:
        template<typename T>
        operator real<T> () const
        {
            return {std::acos(static_cast<T>(-1))};
        }

        template<typename T>
        operator complex<T> () const
        {
            return {std::acos(static_cast<T>(-1))};
        }
};

const pi_t pi = pi_t();

And here this "concept check" is absolutely important, since I won't be overloading every operator for every single constant I decide to provide. This way, I can simply provide a SFINAE enabled overload for the operators and it would be just a matter of inheriting the "concept" to provide new constants.

I can obviously erase the undefined conversion operator on the base class and the problem would be solved, but then it would lose the whole concept idea, which is both to enforce the concept check, by making the program potentially not linkable due to undefined inherited functions, as well as to make it easier for the programer (the lazy me) to get back to this code a couple of years from now and be able to add another concept compliant class, by just looking at the concept itself and realizing what should be provided.


I would expect that the implicit conversion operator to foo declared in convertible_to_foo would hide, i.e. overload,

This mixes up the terms. To hide means that when you look into the derived class, you only see the derived's conversion function and not the base. To overload means that you would see both functions. The function in the derived will hide the function in the base if the derived's function has the same name as the base's function.

The rule for a conversion function names is: Two of them are the same if they convert to the same type. So, if in the base you have operator int, and in the derived class too, then the derived's function will hide the base's one. If the derived's one would be operator float, then you have no hiding. You also have no overloading, but if you lookup in the derived class for a conversion function operator int, then you find the base class's one, because it is not hidden by a derived's function with the same name.

For templates, it works the same

struct A {
  template<typename T, typename U = int>
  operator T();
};

struct B : A {
  template<typename T = int, typename U>
  operator U();
};

In this case, if you try to convert B to an int, you get an ambiguity, because both functions will be taken and both functions will end up to convert to int after template argument deduction is done (T is a different type than U - the first is a template parameter in the first position, while the second is a template parameter in the second position. So no hiding will take place).

In your case, it's similar but I don't agree with GCC's behavior. Instantiating convertible_to<foo> will instantiate the member declarations of that template, and one member will happen to be

template<typename T>
operator foo<T> (); // after substituting "foo" for "C"

The conversion function in the derived class converts to exactly the same type, so I don't think that GCC should yield an ambiguity here.


To answer the first part of your question. In this code

template<template<typename> class C>
class convertible_to
{
    public:
        template<typename T>
        operator C<T> ();
};

class convertible_to_foo : public convertible_to<foo>
{
        public:
        template<typename T>
        operator foo<T>()
        {
            return foo<T>();
        }
};

We cannot really expect that operator C<T> should be hidden if you inherit from convertible_to<foo>, but visible if you had inherited from convertible_to<bar>.

That would make C++ and templates even more complex than it already is. So there are no such rules.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜