Можно ли создать шаблонный гид для вывода типов, учитывающий только второй аргумент?

Можно ли задать первый аргумент явно для класса `Lazy`, чтобы использовать его в следующем примере: 

```cpp
Lazy<Cursor> Arrow = [] { return Cursor(SDL_SYSTEM_CURSOR_ARROW); };
```

при этом сохранив возможность использовать шаблонный параметр `TFactory`, как показано в следующем коде: 

```cpp
template <typename T, typename TFactory = DefaultValueFactory<T>>
class Lazy final { /*...*/};
``` 

Ведут ли изменения к улучшению читаемости и контролю параметров шаблона в данном случае?
  • 5 апреля 2025 г. 6:04
Ответы на вопрос 2
Да, вы можете создать шаблонный класс `Lazy`, который будет иметь первый аргумент явно заданным, а второй аргумент — параметром шаблона с умолчанием. Это позволит вам задать необходимую функциональность, связанную с `TFactory`, при этом сохранив возможность явно указывать тип для `Lazy`.

Ваш класс можно реализовать следующим образом:

```cpp
template <typename TFactory = DefaultValueFactory<Cursor>>
class Lazy final {
public:
    using ResultType = Cursor;

    Lazy(std::function<ResultType()> factory)
        : factory_(factory) {}

    ResultType get() {
        if (!isInitialized_) {
            value_ = factory_();
            isInitialized_ = true;
        }
        return value_;
    }

private:
    std::function<ResultType()> factory_;
    ResultType value_;
    bool isInitialized_ = false;
};
```

Теперь, когда вы создаете экземпляр `Lazy` с типом `Cursor`, вы можете указать только второй аргумент, например:

```cpp
Lazy<> Arrow = [] { return Cursor(SDL_SYSTEM_CURSOR_ARROW); };
```

Или, если вам нужно явно указать фабрику:

```cpp
Lazy<MyCustomFactory> Arrow = [] { return Cursor(SDL_SYSTEM_CURSOR_ARROW); };
```

Что касается читаемости и контроля параметров шаблона, такой подход подойдет, если:

1. Вы всегда используете один и тот же тип для первого аргумента, что упрощает использование класса и предотвращает возможные ошибки с типами.
2. Вы хотите сохранить возможность использовать разные фабрики, если у вас есть инструменты для создания объектов и вам нужно это разнообразие.

Благодаря этому конструкция становится более понятной, поскольку вы явно разделяете параметры шаблона на два, и это может улучшить понимание кода другими программистами, особенно если они знают, что `T` будет всегда типом `Cursor`. Однако, как и везде, всё зависит от контекста использования и предпочтений команды разработчиков, поэтому важно учитывать конкретные требования вашего проекта.
Пока получилось через функцию. 

#include <utility>
#include <iostream>

class Cursor {
public:
    Cursor(int aShape) noexcept : fShape(aShape) {}
    int shape() const noexcept { return fShape; }
private:
    int fShape;
};

template <typename T, typename TFactory>
class Lazy {
public:
    Lazy(TFactory&& x) : factory(std::move(x)) {}
    Lazy(const TFactory& x) : factory(x) {}
    T operator()() const { return factory(); }
private:
    mutable TFactory factory;
};

template <typename T, typename TFactory>
inline auto lazy(TFactory&& factory)
{
    return Lazy<T, std::decay_t<TFactory>>(std::forward<TFactory>(factory));
}

int main()
{
    auto arrow = lazy<Cursor>([] { return Cursor(42); } );
    std::cout << arrow().shape() << '\n';
    return 0;
}


Может, шаблонного полиморфизма вам хватит, но я бы довольно быстро прикрутил виртуальный полиморфизм.
Похожие вопросы