Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiation of the function definition concept-constrained template

I am trying to implement the template using globally overloaded function to perform custom actions per signature. Since there could be group of types (eg. numeric types) having a similar implementation I tried to use concepts to cover it, but stumbled into the problem where the compiler doesn't see the definition with concept without explicit instantiation.

MSVC 17.13.0 (/std:c++latest)

// my.hpp
template <typename value_type>
void bar(value_type value);

template <typename value_type>
void foo(value_type value)
{
    bar<value_type>(value);
}
// my.cpp
#include "my.hpp"
#include <print>
#include <type_traits>

template<std::integral value_type>
void bar(value_type value)
{
    std::println("bar(integral[{}])", value);
}

int main()
{
    //bar(5);
    foo(6);
}

The program above does not build because the linker doesn't see bar's definition:

unresolved external symbol "void __cdecl bar(int)" (??$bar@H@@YAXH@Z) referenced in function "void __cdecl foo(int)" (??$foo@H@@YAXH@Z)

Though, if I uncomment the line bar(5); in main(), it will obviously instantiate the concept-constrained template, and the result will be proper (but with extra output from direct call bar(5)):

bar(integral[5])

bar(integral[6])

Is there common pattern for solving such a task? Basically, I want that the template receiving the type was able to find the suitable definition of template function itself, void bar<int> in my case.

like image 661
Symon Avatar asked Dec 06 '25 15:12

Symon


1 Answers

First,

template <typename      value_type> void bar(value_type value); // #1
template <std::integral value_type> void bar(value_type value); // #2

are two different overloads.

Second, so in

template <typename value_type> void bar(value_type value); // #1

template <typename value_type>
void foo(value_type value)
{
    bar<value_type>(value);
}

Unless ADL kicks in, bar<value_type>(value); refers to #1 (the only one visible). As you use value_type=int, ADL (Argument-Dependent Lookup) is not involved.

If you add dummy parameter to enable ADL, then you might have:

struct adl_enabler{};

template <typename value_type>
void foo(value_type value)
{
    bar<value_type>(value, adl_enabler{});
}

template <std::integral value_type>
void bar(value_type value, adl_enabler) // in same namespace than `adl_enabler`
                                        // (global namespace here)
{
    std::println("bar(integral[{}])", value);
}

Demo

like image 118
Jarod42 Avatar answered Dec 08 '25 05:12

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!