1

Say we have a class definition

class Foo {...};

I know we can reuse its definition with using or typedef

using Bar = Foo; // or typedef Foo Bar;

But they are treat as a same type, that is to say the compiler treat following as redefinition

void fun(const Foo&) { cout<<"It is Foo"<<endl; }
void fun(const Bar&) { cout<<"It is Bar"<<endl; }

Is there any possible or work around we can reuse definition that compiler treat as different types


Thanks for your solution and discussion Here are some details of my case: classes would declare their related type. and the type could be the same.

struct MyFoo { using type = SomeRelated; };
struct MyBar { using type = SomeRelated; };

Another class would be contructed with the related type of above classes and I want the constructor can distinguish from them

struct MyClass
{
    MyClass(const MyFoo::type&) {cout<<"ctor from MyFoo"<<endl;}
    MyClass(const MyBar::type&) {cout<<"ctor from MyBar"<<endl;}
};
hczstev
  • 41
  • 4
  • *Is there any possible or work around* -- We don't know the high-level problem you are trying to solve. Obviously using the same types isn't going to work. – PaulMcKenzie May 30 '23 at 01:41
  • 1
    No. Whether doing `using Bar = Foo` or `typedef Foo Bar`, the name `Bar` is simply an alternative name (pseudonym) for the type `Foo`, not a distinct type. There are options to have the behaviour of `fun()` vary with type of its argument (e.g. `Foo` a polymorphic type, and `Bar` override `virtual` member functions provided by `Foo`) - but what works in such cases depends on *actual* needs of your program, which you have not described. – Peter May 30 '23 at 01:42
  • 1
    `using` and `typedef` establish aliases, alternate names, for existing types. They do not make new types. Here is a decent discussion of some of the alternatives to get what you want: [Making identical C++ type aliases incompatible](https://stackoverflow.com/q/41300364/4581301) – user4581301 May 30 '23 at 01:42
  • Can you use the Boost [strong typedef](https://www.boost.org/doc/libs/1_82_0/boost/serialization/strong_typedef.hpp)? – Eljay May 30 '23 at 01:47
  • 2
    What is the real problem that you're trying to solve? No, not how to "reuse definition that compiler treat as different types", but the real problem to which you believe this is the solution, so that's what you're asking about. Perhaps if the real problem is explained then a different C++ solution can be devised. – Sam Varshavchik May 30 '23 at 01:52
  • If you want two different classes, make two different classes. If really needed derive 2 classes from one shared base class (for now). Using type aliasing is just not enough. – Pepijn Kramer May 30 '23 at 03:34

3 Answers3

4

You can make it with help of a class template, template instances are always different types.

template <int>
class Impl {
  // Implement like your Foo here
};

using Foo = Impl<1>;
using Bar = Impl<2>;

Or with the inherence (you don't need to recall the last used number above).

template <typename>
class Impl {
  // Implement like your Foo here
};

class Foo : public Impl<Foo> {};
class Bar : public Impl<Bar> {};
273K
  • 29,503
  • 10
  • 41
  • 64
  • Question: I know that the person who asked the question didn't specify or give more details about the problem, but could you make `Foo` the base class and `Bar` the derived class (w/o templates)? The compiler would treat them as distinct types right? – underloaded_operator May 30 '23 at 01:49
  • @TheCompile-TimeComedian not really. That doesn't make distinct unrelated types, because then `Bar` is a kind of `Foo`. That means in the absence of something that receives a `Bar&` value, a `Foo&` parameter could be matched instead. This may be undesirable, if the actual goal is for the compiler to reject it. – paddy May 30 '23 at 02:56
  • Oh shoot, yeah, you're right. – underloaded_operator May 30 '23 at 02:59
1

This is known as the strong typedef idiom/problem. There exist many partial solutions, but mostly fundamental C++ types are considered. For class types, it is easier to use inheritance:

class foo;
class bar : public foo
{ using foo::foo; }; //enable all foo constructors

For other types a combination of CRTP and boxed value can be used:

#include <type_traits>
template<typename this_type, typename value_type>
    requires(std::is_final_v<value_type> || 
         not std::is_class_v<value_type>)
struct strong_alias
{ value_type value; };

struct my_int: strong_alias<my_int,int>{};

my_int i{3};
i.value += 4;

Other approaches are available too, but they generally lead to complex and hard to read code.

Red.Wave
  • 2,790
  • 11
  • 17
0

You cannot make them different by simply using re-define-type.

But if you are trying to pass same type to functions having same name but different implementation, give a tuning to function parameter signature. Try the code below.

#include <iostream>

class foo {
};

void test(const foo&)      { std::cout << "void test(const foo&)\n"; }
void test(const foo&, int) { std::cout << "void test(const foo&, int)\n"; }

int main() {
    foo f;
    test(f);
    test(f, 0);
    return 0;
}
kwikc
  • 72
  • 4