szablon funkcji dla integer i string

szablon funkcji dla integer i string
AD
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0
Kopiuj
auto sum() { return 0; }
template <typename T, typename ... U>
auto sum(T t, U ... u)
{
return t + sum(u ...);
}

.... ....

printf("%d", sum(5, 12, 4, 5));
printf("%s", sum("abc", "de", "fghij", "k")); // to nie działa

Liczby ładnie sumuje, ale stringów już nie. Da się zmusić ten szablon aby ta sama funkcja sumowała liczby i stringi jak wyżej ??

Kopiuj
template<typename T, typename U>
decltype(declval<T>() + declval<U>()) suma(T t, U u)
{
  return t + u;
}

Natomiast ta funkcja sumuje mi zarówno liczby jak i stringi. Tylko jak ją przerobić, żeby zamiast dwóch parametrów miała dowolną ilość parametrów ??

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
4

Problem jest typ literałów.
Literały mają typ const char[] a ten typ (odziedziczony po C) nie ma operatora dodawania napisów.

Można do tego podejść na kilka sposobów.
Moim zdaniem najlepiej wykonać specjalizację dla napisów.

Pytanie jest jakiego standardu c++ używasz? Od C++20 są koncepty, które uczynią tą specjalizację bardziej czytelną i szybszą.
Od C++17 jest "fold expression", które również upraszcza kod.

SL
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1081
0
Kopiuj
printf("%s", sum(std::string{"abc"}, "de", "fghij", "k"))
AD
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0

C++17. A da się to drugie przerobić tak, żeby miało dowolną liczbę parametrów funkcji ??

MarekR22
  • Rejestracja: dni
  • Ostatnio: dni
3
Kopiuj
namespace foo {
template <typename T>
struct is_string_like : std::false_type {};

template <>
struct is_string_like<std::string> : std::true_type {};

template <>
struct is_string_like<std::string_view> : std::true_type {};

template <>
struct is_string_like<const char*> : std::true_type {};

template <std::size_t N>
struct is_string_like<const char[N]> : std::true_type {};

template <typename T>
inline constexpr bool is_string_like_v = is_string_like<std::decay_t<T>>::value;

template <typename... Ts>
auto sum(Ts&&... xs) {
    static_assert(sizeof...(Ts) > 0, "sum requries at least one argument");

    constexpr bool all_arithmetic =
        (std::is_arithmetic_v<std::decay_t<Ts>> && ...);

    constexpr bool all_strings = (is_string_like_v<Ts> && ...);

    static_assert(all_arithmetic || all_strings,
                  "All arguments mut be artmitic type or string like type");

    if constexpr (all_arithmetic) {
        return (std::forward<Ts>(xs) + ...);
    } else {
        std::string result;
        result.reserve((std::string_view{std::forward<Ts>(xs)}.length() + ...));
        (result.append(std::string_view{std::forward<Ts>(xs)}), ...);
        return result;
    }
}
}  // namespace foo

https://godbolt.org/z/bea8fdc43

Albo wersja z użyciem SFINAE
Albo wersja C++20 z konceptami.

Spearhead
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1010
2

A ja się zapytam: dlaczego szablony? Dla każdej kombinacji parametrów wygeneruje to osobny kod: osobno dla dwóch int-ów, osobno dla trzech, czterech i tak dalej. Jak wszystkie argumenty mają być tego samego typu to się trochę mija z celem, skoro można po prostu przerzucić argumenty w liście inicjalizacyjnej. Składnia może nieco mniej czytelna, ale kod nie będzie puchnął

Kopiuj
#include <iostream>
#include <numeric>
#include <string>
     
using namespace std::string_literals;     
    
template<typename T>
T sum(std::initializer_list<T> args) {
    return std::accumulate(args.begin(), args.end(), T{});
}

int main() {
    std::cout << sum({1, 2, 3}) << "\n";
    std::cout << sum({"a"s, "b"s, "c"s}) << "\n";
}

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.