Search code examples
c++templatesc++builder

In C++, is it possible to override a single data type in a template


I wrote a sample application to point out what I am trying to do. I have a template that I want to behave differently for a single data type and I would like to hide that from the calling program. In the code below, the intention is that if the data type is UnicodeString, I want it to use my override function; otherwise, I want it to use the template. It would also work to just modify the template and get rid of my override function, if that is possible.

If TRY_TO_OVERRIDE_TEMPLATE is defined, it looks like the compiler gives up because it can't decide which template to use.

If DO_NOT_EXPLICITLY_REFERENCE_TEMPLATE is defined, it causes a compile time error.

If neither of the defines are defined, it does a string concatenation, rather than converting each string to integer and adding the numerical values together.

I am using the current edition of C++ Builder, by the way.

//#define TRY_TO_OVERRIDE_TEMPLATE // results in "Unresolved external 'System::UnicodeString __fastcall MyMath::MyAddSystem::UnicodeString(System::UnicodeString, System::UnicodeString)' referenced from ...\UNIT1.OBJ" //#define DO_NOT_EXPLICITLY_REFERENCE_TEMPLATEclass MyMath {
public:
    MyMath() {};
    #ifdef TRY_TO_OVERRIDE_TEMPLATE
        template <typename T> UnicodeString __fastcall MyAdd(UnicodeString a, UnicodeString b);
    #else
        UnicodeString __fastcall MyAdd(UnicodeString a, UnicodeString b);
    #endif
    template <typename T> T __fastcall MyAdd(T a, T b);
};

#ifdef TRY_TO_OVERRIDE_TEMPLATE
    template <typename T> UnicodeString __fastcall MyAdd(UnicodeString a, UnicodeString b) {
        int intA = a.ToInt();
        int intB = b.ToInt();
        return UnicodeString(a + b);
    }
#else
    UnicodeString __fastcall MyAdd(UnicodeString a, UnicodeString b) {
        int intA = a.ToInt();
        int intB = b.ToInt();
        return UnicodeString(a + b);
    }
#endif

template <typename T> T __fastcall MyMath::MyAdd(T a, T b) {
    return a + b;     // When defined DO_NOT_EXPLICITLY_REFERENCE_TEMPLATE, this gives standard error concatinating UnicodeStrings ([bcc32c Error] Unit1.cpp(41): invalid operands to binary expression ('const wchar_t *' and 'const wchar_t *')
}

void __fastcall TForm1::cmdRunClick(TObject *Sender)
{
    UnicodeString txt;

    MyMath * mth = new MyMath();

    ComboBox1->Items->Add(L"1 + 3 =" + UnicodeString(mth->MyAdd<int>(1, 3)));
    ComboBox1->Items->Add(L"1.1 + 3.3 =" + UnicodeString(mth->MyAdd<double>(1.1, 3.3)));
    ComboBox1->Items->Add(L"2.2 + 4 =" + UnicodeString(mth->MyAdd<UnicodeString>(L"2.2", L"4")));  // result is "2.24" rather than "6"
    #ifdef DO_NOT_EXPLICITLY_REFERENCE_TEMPLATE
        ComboBox1->Items->Add(L"2.2 + 4 =" + UnicodeString(mth->MyAdd(L"2.2", L"4")));
    #endif
}

Solution

  • You can rely on standard overloading rules. If you want a specific Add for Unicode strings then provide an overload next to the generic template you already have, and it will be selected at compile time.

    #include <type_traits>
    #include <string>
    #include <iostream>
    #include <format>
    
    using namespace std::string_literals;
    
    template<typename type_t>
    auto add(const type_t& lhs, const type_t& rhs)
    {
        std::cout << "Generic add : " << lhs << " + " << rhs << "\n";;
        return lhs + rhs;
    }
    
    // you can have non-template overloads too
    // they will be selected in favor of the template
    // when they match.
    auto add(const std::string& lhs, const std::string& rhs)
    {
        auto result = std::format("String addition : '{}' + {}", lhs, rhs);
        std::cout << result << "\n";
        return result;
    }
    
    int main()
    {
        add(1, 2);
        add("one"s, "two"s);
        return 0;
    }