如何根据可调用对象的调用签名重载函数模板?
Ah, I see the issue here! Your two fun templates have identical template parameter lists and function parameter lists, which is why the compiler can't tell them apart—template overload resolution doesn't look at the function body, only the template/function signatures. Let's fix this properly with different approaches depending on your C++ version.
Solution 1: C++17+ with constexpr if (Simplest Single Template)
This approach uses a single function template with compile-time branching to handle different callable signatures—no overload conflicts to worry about:
#include <type_traits> #include <iostream> class A { public: double operator()(double foo) { return foo; } }; class B { public: double operator()(double foo, int bar) { return foo + bar; } }; template <typename F, typename T> T fun(F f, T t) { // Check if the callable accepts a single T argument if constexpr (std::is_invocable_r_v<T, F, T>) { return f(t); } // Check if the callable accepts T and int arguments else if constexpr (std::is_invocable_r_v<T, F, T, int>) { return f(t, 2); } // Handle unsupported callables (optional but helpful for debugging) else { static_assert(false, "fun() only supports callables that take (T) or (T, int)"); } } int main() { A a; B b; std::cout << fun(a, 5.0) << "\n"; // Outputs 5.0 std::cout << fun(b, 5.0) << "\n"; // Outputs 7.0 return 0; }
How this works:
std::is_invocable_r_v<T, F, Args...>checks if callingfwithArgs...returns a type convertible toT.constexpr ifevaluates the condition at compile time, so only the relevant branch is compiled—no runtime overhead.- A single template eliminates overload conflicts entirely.
Solution 2: C++17+ with SFINAE (Separate Overloads)
If you prefer distinct overloads, use std::enable_if to "disable" templates that don't match the callable's signature:
#include <type_traits> #include <iostream> class A { public: double operator()(double foo) { return foo; } }; class B { public: double operator()(double foo, int bar) { return foo + bar; } }; // Overload for callables that take a single T argument template <typename F, typename T> std::enable_if_t<std::is_invocable_r_v<T, F, T>, T> fun(F f, T t) { return f(t); } // Overload for callables that take T and int arguments template <typename F, typename T> std::enable_if_t<std::is_invocable_r_v<T, F, T, int>, T> fun(F f, T t) { return f(t, 2); } int main() { A a; B b; std::cout << fun(a, 5.0) << "\n"; // Uses first overload std::cout << fun(b, 5.0) << "\n"; // Uses second overload return 0; }
How this works:
std::enable_if_t<Condition, Type>only defines the function return type ifConditionistrue. If false, the template is ignored during overload resolution.- The two overloads have mutually exclusive conditions (for your
AandBclasses), so the compiler picks the correct one every time.
Solution 3: C++20 with Concepts (Most Readable)
C++20 Concepts make this code self-documenting and clean, explicitly defining requirements for each overload:
#include <concepts> #include <iostream> class A { public: double operator()(double foo) { return foo; } }; class B { public: double operator()(double foo, int bar) { return foo + bar; } }; // Concept for callables that take a single T and return a type convertible to T template <typename F, typename T> concept SingleArgCallable = std::invocable<F, T> && std::convertible_to<std::invoke_result_t<F, T>, T>; // Concept for callables that take T+int and return a type convertible to T template <typename F, typename T> concept TwoArgCallable = std::invocable<F, T, int> && std::convertible_to<std::invoke_result_t<F, T, int>, T>; template <SingleArgCallable F, typename T> T fun(F f, T t) { return f(t); } template <TwoArgCallable F, typename T> T fun(F f, T t) { return f(t, 2); } int main() { A a; B b; std::cout << fun(a, 5.0) << "\n"; // Matches SingleArgCallable std::cout << fun(b, 5.0) << "\n"; // Matches TwoArgCallable return 0; }
How this works:
- Concepts act as compile-time checks that clearly state what each overload expects from the callable.
- The compiler only considers an overload if the callable meets the concept's requirements, eliminating ambiguity.
Why your original code failed:
Your two fun templates have identical template parameters (typename F, typename T) and function parameters (F f, T t). Template overload resolution doesn't inspect the function body—only the signature. Since the signatures are identical, the compiler sees them as duplicate definitions, hence the conflict.
内容的提问来源于stack exchange,提问作者texdditor

