Hide-by-Signature Functions in Reference Types

In standard C++, a function in a base class will be hidden by a function with the same name in a derived class, even if the derived class function does not have the same number or type of parameters. This is referred to as hide-by-name semantics. In reference types, a function in a base class can only be hidden by a function in a derived type if the name and parameter list are the same. This is called hide-by-signature semantics.

Remarks

A class is considered a hide-by-signature class when all its functions are marked in the metadata as hidebysig. By default, all classes created under /clr have hidebysig functions. However, a class compiled with /clr:oldSyntax does not have hidebysig functions, they are hide by name functions. When a class has hidebysig functions, the compiler does not hide functions by name in any direct base classes. However, once the compiler encounters a hide-by-name class in an inheritance chain, it resumes hide by name behavior.

Using hide-by-signature semantics, when a function is called on an object, the compiler identifies the most derived class containing a function that could satisfy the function call. If there is only one function in the class that could satisfy the call, the compiler calls that function. If there is more than one function in the class that could satisfy the call, the compiler uses overload resolution rules to determine which function to call. For more information on overload rules, see Function Overloading.

A function in a base class may even have a signature that makes it a slightly better match than a function in a derived class, for a given function call. However, if the function was explicitly called on an object of the derived class, the function in the derived class will be called.

Because the return value is not considered part of a function's signature, a base class function will be hidden if it has the same name and takes the same number and type of arguments as a derived class function, but only differs in the type of the return value.

Example

The following sample shows that a function in a base class is not hidden by a function in a derived class.

// hide_by_signature_1.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   void Test() { 
      Console::WriteLine("Base::Test"); 
   }
};

ref struct Derived : public Base {
   void Test(int i) { 
      Console::WriteLine("Derived::Test"); 
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Test() in the base class will not be hidden
   t->Test();
}

Base::Test

The following sample shows that the Visual C++ compiler will call a function in the most derived class, even if a conversion is required to match one or more of the parameters, and not call a function in a base class that is a better match for the function call.

// hide_by_signature_2.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   void Test2(Single d) { 
      Console::WriteLine("Base::Test2"); 
   }
};

ref struct Derived : public Base {
   void Test2(Double f) { 
      Console::WriteLine("Derived::Test2"); 
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Base::Test2 is better match, but the compiler
   // will call a function in derived class if possible
   t->Test2(3.14f);
}

Derived::Test2

The following sample shows that it is still possible to hide a function if the base class has the same signature as the derived class.

// hide_by_signature_3.cpp
// compile with: /clr
using namespace System;
ref struct Base {
   int Test4() { 
      Console::WriteLine("Base::Test4"); 
      return 9; 
   }
};

ref struct Derived : public Base {
   char Test4() { 
      Console::WriteLine("Derived::Test4"); 
      return 'a'; 
   }
};

int main() {
   Derived ^ t = gcnew Derived;

   // Base::Test4 is hidden
   int i = t->Test4();
   Console::WriteLine(i);
}

Derived::Test4 97

The following sample defines a component compiled with /clr:oldSyntax. Classes defined using Managed Extensions for C++ have hide-by-name member functions.

// hide_by_signature_4.cpp
// compile with: /clr:oldSyntax /LD
using namespace System;
public __gc struct Base0 {
   void Test() { 
      Console::WriteLine("in Base0::Test");
   }
};

public __gc struct Base1 : public Base0 {
   void Test(int i) { 
      Console::WriteLine("in Base1::Test");
   }
};

The following sample consumes the component built in the previous sample. Notice how hide-by-signature functionality is not applied to base classes of types compiled with /clr:oldSyntax.

// hide_by_signature_5.cpp
// compile with: /clr:oldSyntax /LD
// compile with: /clr
using namespace System;
#using "hide_by_signature_4.dll"

ref struct Derived : public Base1 {
   void Test(int i, int j) { 
      Console::WriteLine("Derived::Test");
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   t->Test(8, 8);   // OK
   t->Test(8);   // OK
   t->Test();   // C2661
}

See Also

Reference

Classes and Structs (Managed)