friend std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);
declares a non template version friend, so you would have to implement
std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);
for every T you use:
std::ostream& operator<<(std::ostream& os, const Derived<int>& dt) {
    return os << dt.data_;
}
And so on.
A way to do it without duplicate code is with definition inside the class:
template <typename T>
class Derived : public IBase
{
public:
    explicit Derived(T data);
    friend std::ostream& operator<<(std::ostream& os, const Derived<T>& dt)
    {
        return os << dt.data_;
    }
private:
    T data_;
};
Demo
An other alternative is to make the function template.
// Forward declarations
template <typename T> class Derived;
template <typename T> std::ostream& operator<<(std::ostream& os, const Derived<T>& dt);
And then in Derived, you have 2 choices:
- Declare each the template method friend: - template <typename T2>
friend std::ostream& operator<<(std::ostream& os, const Derived<T2>& dt);
 - So - std::ostream& operator<<(std::ostream& os, const Derived<int>& dt)has access to private members of- Derived<int>, but also to the ones of- Derived<char>
 - Demo 
- Declare only the template which matches the argument: - friend std::ostream& operator<< <>(std::ostream& os, const Derived& dt);
 - and so - std::ostream& operator<<(std::ostream& os, const Derived<int>& dt)doesn't have access to private members of- Derived<char>.
 - Demo